#include "arc/std/queue.h"

#include "arc/std/errno.h"
#include <stdlib.h>

typedef struct ARC_QueueNode ARC_QueueNode;

struct ARC_QueueNode {
    ARC_QueueNode *next;
    void *data;
};

struct ARC_Queue {
    uint32_t currentSize;
    ARC_QueueNode *node;
};

void ARC_Queue_Create(ARC_Queue **queue){
    *queue = (ARC_Queue *)malloc(sizeof(ARC_Queue));
    (*queue)->currentSize = 0;
    (*queue)->node = NULL;
}

void ARC_Queue_Destroy(ARC_Queue *queue){
    if(queue->currentSize != 0 || queue->node != NULL){
        arc_errno = ARC_ERRNO_DATA;
        ARC_DEBUG_LOG_ERROR("ARC_Queue_Destroy(queue) called, but queue was not empty");
        return;
    }

    free(queue);
}

void ARC_Queue_Push(ARC_Queue *queue, void *data){
    if(queue->currentSize == ~(uint32_t)0){
        arc_errno = ARC_ERRNO_OVERFLOW;
        ARC_DEBUG_LOG_ERROR("ARC_Queue_Push(queue) called, size of queue is maxed, cannot add another node");
        return;
    }

    if(queue->currentSize == 0){
        ARC_QueueNode *node = (ARC_QueueNode *)malloc(sizeof(ARC_QueueNode));
        node->data = data;
        node->next = NULL;

        queue->node = node;
        queue->currentSize++;

        return;
    }

    ARC_QueueNode *end = queue->node;
    for(uint32_t i = 1; i < queue->currentSize; i++){
        if(end->next == NULL){
            arc_errno = ARC_ERRNO_NULL;
            ARC_DEBUG_LOG_ERROR("ARC_Queue_Push(queue), when getting end node encountered a NULL node");
            return;
        }

        end = end->next;
    }

    ARC_QueueNode *node = (ARC_QueueNode *)malloc(sizeof(ARC_QueueNode));
    node->data = data;
    node->next = NULL;

    end->next = node;

    queue->currentSize++;
}

void *ARC_Queue_Pop(ARC_Queue *queue){
    if(queue->currentSize == 0){
        arc_errno = ARC_ERRNO_DATA;
        ARC_DEBUG_LOG_ERROR("ARC_Queue_Pop(queue) called, but queue was empty");
        return NULL;
    }

    ARC_QueueNode *node = queue->node;
    if(node == NULL){
        arc_errno = ARC_ERRNO_NULL;
        ARC_DEBUG_LOG_ERROR("ARC_Queue_Pop(queue) called, but node was NULL");
        return NULL;
    }

    queue->node = node->next;
    void *nodeData = node->data;
    free(node);

    queue->currentSize--;

    return nodeData;
}

uint32_t ARC_Queue_Size(ARC_Queue *queue){
    return queue->currentSize;
}