/*
list.c

(c) TuxSH, 2017
This is part of 3ds_sm, which is licensed under the MIT license (see LICENSE for details).
*/

#include "list.h"
#include "memory.h"
#include "common.h"

struct ListBase;
typedef struct ListNodeBase
{
    struct ListNodeBase *prev, *next;
    struct ListBase *parent;
} ListNodeBase;

typedef struct ListBase
{
    ListNodeBase *first, *last;
} ListBase;

void buildList(void *list, void *pool, u32 nb, u32 elementSize)
{
    ListBase *listB = (ListBase *)list;
    for(u32 i = 0; i < nb; i++)
    {
        ListNodeBase *node = (ListNodeBase *)((u8 *)pool + i * elementSize);
        node->prev = i == 0 ? NULL : (ListNodeBase *)((u8 *)pool + (i - 1) * elementSize);
        node->next = i == (nb - 1) ? NULL : (ListNodeBase *)((u8 *)pool + (i + 1) * elementSize);
        node->parent = list;
    }

    listB->first = (ListNodeBase *)pool;
    listB->last = (ListNodeBase *)((u8 *)pool + (nb - 1) * elementSize);
}

void moveNode(void *node, void *dst, bool back)
{
    ListNodeBase *nodeB = (ListNodeBase *)node;
    ListBase *dstB = (ListBase *)dst;
    ListBase *srcB = nodeB->parent;

    if(dstB == srcB)
        return;

    // Remove the node in the source list
    if(nodeB->prev != NULL)
        nodeB->prev->next = nodeB->next;

    if(nodeB->next != NULL)
        nodeB->next->prev = nodeB->prev;

    // Update the source list if needed
    if(nodeB == srcB->first)
        srcB->first = nodeB->next;
    if(nodeB == srcB->last)
        srcB->last = nodeB->prev;

    // Insert the node in the destination list
    if(back)
    {
        if(dstB->last != NULL)
            dstB->last->next = nodeB;

        nodeB->prev = dstB->last;
        nodeB->next = NULL;
        dstB->last = nodeB;
    }
    else
    {
        if(dstB->first != NULL)
            dstB->first->prev = nodeB;

        nodeB->next = dstB->first;
        nodeB->prev = NULL;
        dstB->first = nodeB;
    }

    // Normalize the destination list
    if(dstB->first != NULL && dstB->last == NULL)
        dstB->last = dstB->first;
    else if(dstB->first == NULL && dstB->last != NULL)
        dstB->first = dstB->last;

    nodeB->parent = dstB;
}

void *allocateNode(void *inUseList, void *freeList, u32 elementSize, bool back)
{
    ListBase *freeListB = (ListBase *)freeList;

    if(freeListB->first == NULL)
        panic();

    ListNodeBase *node = freeListB->first;
    ListNodeBase nodeBk = *node;

    memset(node, 0, elementSize);
    *node = nodeBk;

    moveNode(node, inUseList, back);

    return node;
}
