/*
 * This file is part of the PCEPlib, a PCEP protocol library.
 *
 * Copyright (C) 2020 Volta Networks https://voltanet.io/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Author : Brady Johnson <brady@voltanet.io>
 *
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#include "pcep_utils_logging.h"
#include "pcep_utils_memory.h"
#include "pcep_utils_queue.h"

queue_handle *queue_initialize()
{
	/* Set the max_entries to 0 to disable it */
	return queue_initialize_with_size(0);
}


queue_handle *queue_initialize_with_size(unsigned int max_entries)
{
	queue_handle *handle =
		pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_handle));
	memset(handle, 0, sizeof(queue_handle));
	handle->max_entries = max_entries;

	return handle;
}


void queue_destroy(queue_handle *handle)
{
	if (handle == NULL) {
		pcep_log(
			LOG_DEBUG,
			"%s: queue_destroy, the queue has not been initialized",
			__func__);
		return;
	}

	while (queue_dequeue(handle) != NULL) {
	}
	pceplib_free(PCEPLIB_INFRA, handle);
}


void queue_destroy_with_data(queue_handle *handle)
{
	if (handle == NULL) {
		pcep_log(
			LOG_DEBUG,
			"%s: queue_destroy_with_data, the queue has not been initialized",
			__func__);
		return;
	}

	void *data = queue_dequeue(handle);
	while (data != NULL) {
		pceplib_free(PCEPLIB_INFRA, data);
		data = queue_dequeue(handle);
	}
	pceplib_free(PCEPLIB_INFRA, handle);
}


queue_node *queue_enqueue(queue_handle *handle, void *data)
{
	if (handle == NULL) {
		pcep_log(
			LOG_DEBUG,
			"%s: queue_enqueue, the queue has not been initialized",
			__func__);
		return NULL;
	}

	if (handle->max_entries > 0
	    && handle->num_entries >= handle->max_entries) {
		pcep_log(
			LOG_DEBUG,
			"%s: queue_enqueue, cannot enqueue: max entries hit [%u]",
			handle->num_entries);
		return NULL;
	}

	queue_node *new_node =
		pceplib_malloc(PCEPLIB_INFRA, sizeof(queue_node));
	memset(new_node, 0, sizeof(queue_node));
	new_node->data = data;
	new_node->next_node = NULL;

	(handle->num_entries)++;
	if (handle->head == NULL) {
		/* its the first entry in the queue */
		handle->head = handle->tail = new_node;
	} else {
		handle->tail->next_node = new_node;
		handle->tail = new_node;
	}

	return new_node;
}


void *queue_dequeue(queue_handle *handle)
{
	if (handle == NULL) {
		pcep_log(
			LOG_DEBUG,
			"%s: queue_dequeue, the queue has not been initialized",
			__func__);
		return NULL;
	}

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

	void *node_data = handle->head->data;
	queue_node *node = handle->head;
	(handle->num_entries)--;
	if (handle->head == handle->tail) {
		/* its the last entry in the queue */
		handle->head = handle->tail = NULL;
	} else {
		handle->head = node->next_node;
	}

	pceplib_free(PCEPLIB_INFRA, node);

	return node_data;
}
