#include "StdAfx.h"
#ifndef ACL_PREPARE_COMPILE

#include "stdlib/acl_define.h"
#include <stdlib.h>
#include "stdlib/acl_mymalloc.h"
#include "stdlib/acl_msg.h"

#ifdef ACL_BCB_COMPILER
#pragma hdrstop
#endif

#include "stdlib/acl_fifo.h"

#endif

static void fifo_push_back(ACL_FIFO *fifo, void *data)
{
	acl_fifo_push_back(fifo, data);
}

static void fifo_push_front(ACL_FIFO *fifo, void *data)
{
	acl_fifo_push_front(fifo, data);
}

static void* fifo_pop_back(ACL_FIFO *fifo)
{
	return acl_fifo_pop_back(fifo);
}

static void* fifo_pop_front(ACL_FIFO *fifo)
{
	return acl_fifo_pop_front(fifo);
}

static void *fifo_iter_head(ACL_ITER *iter, struct ACL_FIFO *fifo)
{
	ACL_FIFO_INFO *ptr;

	iter->dlen = -1;
	iter->key = NULL;
	iter->klen = -1;
	iter->i = 0;
	iter->size = fifo->cnt;
	iter->ptr = ptr = fifo->head;
	iter->data = ptr ? ptr->data : NULL;
	return iter->ptr;
}

static void *fifo_iter_next(ACL_ITER *iter, struct ACL_FIFO *fifo acl_unused)
{
	ACL_FIFO_INFO *ptr;

	ptr = (ACL_FIFO_INFO*) iter->ptr;
	iter->ptr = ptr = ptr ? ptr->next : NULL;
	if (ptr) {
		iter->data = ptr->data;
		iter->i++;
	} else {
		iter->data = NULL;
	}
	return iter;
}

static void *fifo_iter_tail(ACL_ITER *iter, struct ACL_FIFO *fifo)
{
	ACL_FIFO_INFO *ptr;

	iter->dlen = -1;
	iter->key  = NULL;
	iter->klen = -1;
	iter->i    = fifo->cnt - 1;
	iter->size = fifo->cnt;
	iter->ptr  = ptr = fifo->tail;
	iter->data = ptr ? ptr->data : NULL;
	return iter->ptr;
}

static void *fifo_iter_prev(ACL_ITER *iter, struct ACL_FIFO *fifo acl_unused)
{
	ACL_FIFO_INFO *ptr;

	ptr = (ACL_FIFO_INFO*) iter->ptr;
	iter->ptr = ptr = ptr ? ptr->prev : NULL;
	if (ptr) {
		iter->data = ptr->data;
		iter->i--;
	} else {
		iter->data = NULL;
	}
	return iter;
}

static ACL_FIFO_INFO *fifo_iter_info(ACL_ITER *iter, struct ACL_FIFO *fifo acl_unused)
{
	return iter->ptr ? (ACL_FIFO_INFO*) iter->ptr : NULL;
}

void acl_fifo_init(ACL_FIFO *fifo)
{
	fifo->head       = NULL;
	fifo->tail       = NULL;
	fifo->cnt        = 0;
	fifo->slice      = NULL;

	fifo->push_back  = fifo_push_back;
	fifo->push_front = fifo_push_front;
	fifo->pop_back   = fifo_pop_back;
	fifo->pop_front  = fifo_pop_front;
	fifo->iter_head  = fifo_iter_head;
	fifo->iter_next  = fifo_iter_next;
	fifo->iter_tail  = fifo_iter_tail;
	fifo->iter_prev  = fifo_iter_prev;
	fifo->iter_info  = fifo_iter_info;
}

ACL_FIFO *acl_fifo_new(void)
{
	return acl_fifo_new1(NULL);
}

ACL_FIFO *acl_fifo_new1(ACL_SLICE_POOL *slice)
{
	ACL_FIFO *fifo;

	if (slice) {
		fifo = (ACL_FIFO *) acl_slice_pool_alloc(__FILE__, __LINE__,
				slice, sizeof(*fifo));
		fifo->slice = slice;
	} else {
		fifo = (ACL_FIFO *) acl_mymalloc(sizeof(*fifo));
		fifo->slice = NULL;
	}
	fifo->head = NULL;
	fifo->tail = NULL;
	fifo->cnt  = 0;

	fifo->push_back  = fifo_push_back;
	fifo->push_front = fifo_push_front;
	fifo->pop_back   = fifo_pop_back;
	fifo->pop_front  = fifo_pop_front;
	fifo->iter_head  = fifo_iter_head;
	fifo->iter_next  = fifo_iter_next;
	fifo->iter_tail  = fifo_iter_tail;
	fifo->iter_prev  = fifo_iter_prev;

	return fifo;
}

void acl_fifo_free(ACL_FIFO *fifo, void (*free_fn)(void *))
{
	void *data;

	while ((data = acl_fifo_pop(fifo)) != NULL) {
		if (free_fn) {
			free_fn(data);
		}
	}
	if (fifo->slice) {
		acl_slice_pool_free(__FILE__, __LINE__, fifo);
	} else {
		acl_myfree(fifo);
	}
}

ACL_FIFO_INFO *acl_fifo_push_back(ACL_FIFO *fifo, void *data)
{
	ACL_FIFO_INFO *info;

	if (fifo->slice) {
		info = (ACL_FIFO_INFO *) acl_slice_pool_alloc(__FILE__, __LINE__,
				fifo->slice, sizeof(*info));
	} else {
		info = (ACL_FIFO_INFO *) acl_mymalloc(sizeof(*info));
	}
	info->data = data;

	if (fifo->tail == NULL) {
		info->prev = info->next = NULL;
		fifo->head = fifo->tail = info;
	} else {
		fifo->tail->next = info;
		info->prev = fifo->tail;
		info->next = NULL;
		fifo->tail = info;
	}

	fifo->cnt++;
	return info;
}

ACL_FIFO_INFO *acl_fifo_push_front(ACL_FIFO *fifo, void *data)
{
	ACL_FIFO_INFO *info;

	if (fifo->slice) {
		info = (ACL_FIFO_INFO *) acl_slice_pool_alloc(__FILE__, __LINE__,
				fifo->slice, sizeof(*info));
	} else {
		info = (ACL_FIFO_INFO*) acl_mymalloc(sizeof(*info));
	}
	info->data = data;

	if (fifo->head == NULL) {
		info->prev = info->next = NULL;
		fifo->head = fifo->tail = info;
	} else {
		info->next = fifo->head;
		fifo->head->prev = info;
		info->prev = NULL;
		fifo->head = info;
	}

	fifo->cnt++;
	return info;
}

void *acl_fifo_pop_front(ACL_FIFO *fifo)
{
	ACL_FIFO_INFO *info;
	void *data;

	if (fifo->head == NULL) {
		return NULL;
	}

	info = fifo->head;
	if (fifo->head->next) {
		fifo->head->next->prev = NULL;
		fifo->head = fifo->head->next;
	} else {
		fifo->head = fifo->tail = NULL;
	}

	data = info->data;

	if (fifo->slice) {
		acl_slice_pool_free(__FILE__, __LINE__, info);
	} else {
		acl_myfree(info);
	}

	fifo->cnt--;
	return data;
}

void *acl_fifo_pop_back(ACL_FIFO *fifo)
{
	ACL_FIFO_INFO *info;
	void *data;

	if (fifo->tail == NULL) {
		return NULL;
	}

	info = fifo->tail;
	if (fifo->tail->prev) {
		fifo->tail->prev->next = NULL;
		fifo->tail = fifo->tail->prev;
	} else {
		fifo->head = fifo->tail = NULL;
	}
	data = info->data;
	if (fifo->slice) {
		acl_slice_pool_free(__FILE__, __LINE__, info);
	} else {
		acl_myfree(info);
	}
	fifo->cnt--;
	return data;
}

int acl_fifo_delete(ACL_FIFO *fifo, const void *data)
{
	ACL_FIFO_INFO *iter = fifo->head;

	while (iter) {
		if (iter->data == data) {
			if (iter->prev) {
				iter->prev->next = iter->next;
			} else {
				fifo->head = iter->next;
			}
			if (iter->next) {
				iter->next->prev = iter->prev;
			} else {
				fifo->tail = iter->prev;
			}
			if (fifo->slice) {
				acl_slice_pool_free(__FILE__, __LINE__, iter);
			} else {
				acl_myfree(iter);
			}
			fifo->cnt--;
			return 1;
		}

		iter = iter->next;
	}
	return 0;
}

void *acl_fifo_head(ACL_FIFO *fifo)
{
	if (fifo->head) {
		return fifo->head->data;
	} else {
		return NULL;
	}
}

void *acl_fifo_tail(ACL_FIFO *fifo)
{
	if (fifo->tail) {
		return fifo->tail->data;
	} else {
		return NULL;
	}
}

void acl_fifo_free2(ACL_FIFO *fifo, void (*free_fn)(ACL_FIFO_INFO *))
{
	ACL_FIFO_INFO *info;

	while ((info = acl_fifo_pop_info(fifo)) != NULL) {
		if (free_fn) {
			free_fn(info);
		}
	}
	if (fifo->slice) {
		acl_slice_pool_free(__FILE__, __LINE__, fifo);
	} else {
		acl_myfree(fifo);
	}
}

void acl_fifo_push_info_back(ACL_FIFO *fifo, ACL_FIFO_INFO *info)
{
	if (fifo->tail == NULL) {
		info->prev = info->next = NULL;
		fifo->head = fifo->tail = info;
	} else {
		fifo->tail->next = info;
		info->prev = fifo->tail;
		info->next = NULL;
		fifo->tail = info;
	}

	fifo->cnt++;
}

ACL_FIFO_INFO *acl_fifo_pop_info(ACL_FIFO *fifo)
{
	ACL_FIFO_INFO *info;

	if (fifo->head == NULL) {
		return NULL;
	}

	info = fifo->head;
	if (fifo->head->next) {
		fifo->head->next->prev = NULL;
		fifo->head = fifo->head->next;
	} else {
		fifo->head = fifo->tail = NULL;
	}

	fifo->cnt--;
	return info;
}

void acl_fifo_delete_info(ACL_FIFO *fifo, ACL_FIFO_INFO *info)
{
	if (info->prev) {
		info->prev->next = info->next;
	} else {
		fifo->head = info->next;
	}
	if (info->next) {
		info->next->prev = info->prev;
	} else {
		fifo->tail = info->prev;
	}

	if (fifo->slice) {
		acl_slice_pool_free(__FILE__, __LINE__, info);
	} else {
		acl_myfree(info);
	}
	fifo->cnt--;
}

ACL_FIFO_INFO *acl_fifo_head_info(ACL_FIFO *fifo)
{
	if (fifo->head) {
		return fifo->head;
	} else {
		return NULL;
	}
}

ACL_FIFO_INFO *acl_fifo_tail_info(ACL_FIFO *fifo)
{
	if (fifo->tail) {
		return fifo->tail;
	} else {
		return NULL;
	}
}

int acl_fifo_size(ACL_FIFO *fifo)
{
	if (fifo) {
		return fifo->cnt;
	} else {
		return 0;
	}
}
