#include "list.h"

#include <string.h>

struct ListNodeStruct;
struct ListHeaderStruct;

struct ListNodeStruct{
    struct ListHeaderStruct *head;
    struct ListNodeStruct   *prev;
    struct ListNodeStruct   *next;
    char buf[];
};

struct ListHeaderStruct{
    size_t                size;
    uint                  count;
    struct ListNodeStruct data;
};

typedef struct ListNodeStruct   ListNode;
typedef struct ListHeaderStruct ListHeader;


#define getHeader(X)   (*(ListHeader**)(pChar(X)-offsetof(ListNode,buf)))
#define getNode(X)     ((ListNode*)(pChar(X)-offsetof(ListNode,buf)))
#define getNodeSize(X) ((X)->size+sizeof(ListNode))

/******************** private functions *******************/
#define nodeConnect(a,b) \
    do{\
        (a)->next = (b);\
        (b)->prev = (a);\
    }while(0)
#define addNodeFront(lllll) \
    do{\
        ListNode *tzzzmp = (ListNode*)ctl_malloc(getNodeSize(lllll));\
        tzzzmp->head = (lllll);\
        nodeConnect(tzzzmp, (lllll)->data.next);\
        nodeConnect(&(lllll)->data, tzzzmp);\
        (lllll)->count++;\
    }while(0)
#define addNodeBack(lllll) \
    do{\
        ListNode *tzzzmp = (ListNode*)ctl_malloc(getNodeSize(lllll));\
        tzzzmp->head = (lllll);\
        nodeConnect((lllll)->data.prev, tzzzmp);\
        nodeConnect(tzzzmp, &(lllll)->data);\
        (lllll)->count++;\
    }while(0)
#define delNodeFront(lllll) \
    do{\
        ListNode *tzzzmp = (lllll)->data.next;\
        tzzzmp->head = (lllll);\
        nodeConnect(&(lllll)->data, tzzzmp->next);\
        ctl_free(tzzzmp);\
        (lllll)->count--;\
    }while(0)
#define delNodeBack(lllll) \
    do{\
        ListNode *tzzzmp = (lllll)->data.prev;\
        tzzzmp->head = (lllll);\
        nodeConnect(tzzzmp->prev, &(lllll)->data);\
        ctl_free(tzzzmp);\
        (lllll)->count--;\
    }while(0)

/***************** initalize & destructure ****************/
pvoid ctl_list_initX(ppvoid l, size_t size, uint count){
    ListHeader *tmp = (ListHeader*)ctl_malloc(sizeof(ListHeader));
    tmp->size      = size;
    tmp->data.head = tmp;
    tmp->data.prev = &tmp->data;
    tmp->data.next = &tmp->data;
    for(tmp->count = 0; tmp->count < count; tmp->count++){
        addNodeBack(tmp);
    }
    if(l != NULL){
        *l = tmp->data.buf;
    }
    return tmp->data.buf;
}

pvoid ctl_list_freeX(ppvoid l){
    ListHeader* tmp = getHeader(*l);
    while(tmp->data.next != &tmp->data){
        ListNode *ntmp = tmp->data.next;
        tmp->data.next = tmp->data.next->next;
        ctl_free(ntmp);
    }
    ctl_free(tmp);
    *l = NULL;
    return NULL;
}

/**************** about get??? methods ********************/
int ctl_list_getSizeX(ppcvoid l){
    return  getHeader(*l)->count;
}
int ctl_list_getEntrySizeX(ppcvoid l){
    return getHeader(*l)->size;
}
pvoid ctl_list_getFrontX(ppvoid l){
    return getHeader(*l)->data.next->buf;
}
pvoid ctl_list_getBackX(ppvoid l){
    return getHeader(*l)->data.prev->buf;
}
pvoid ctl_list_getBeginX(ppvoid l){
    return getHeader(*l)->data.next->buf;
}
pvoid ctl_list_getEndX(ppvoid l){
    return *l;
}

/**************** about set??? methods ********************/
int ctl_list_setSizeX(ppvoid l, uint count){
    ListHeader *tmp = getHeader(*l);
    while(tmp->count > count){
        delNodeBack(tmp);
    }
    while(tmp->count < count){
        addNodeBack(tmp);
    }
    return tmp->count;
}
pvoid ctl_list_setFrontX(ppvoid l, pcvoid data){
    ListHeader *tmp = getHeader(*l);
    memcpy(tmp->data.next->buf, data, tmp->size);
    return tmp->data.next->buf;
}
pvoid ctl_list_setBackX(ppvoid l, pcvoid data){
    ListHeader *tmp = getHeader(*l);
    memcpy(tmp->data.prev->buf, data, tmp->size);
    return tmp->data.prev->buf;
}

/**************** about add/del front/back ****************/
int ctl_list_addFrontX(ppvoid l, pcvoid data){
    ListHeader* tmp = getHeader(*l);
    addNodeFront(tmp);
    ctl_list_setFrontX(l, data);
    return tmp->count;
}
int ctl_list_delFrontX(ppvoid l){
    ListHeader *tmp = getHeader(*l);
    delNodeFront(tmp);
    return tmp->count;
}
int ctl_list_addBackX(ppvoid l, pcvoid data){
    ListHeader* tmp = getHeader(*l);
    addNodeBack(tmp);
    ctl_list_setBackX(l, data);
    return tmp->count;
}
int ctl_list_delBackX(ppvoid l){
    ListHeader *tmp = getHeader(*l);
    delNodeBack(tmp);
    return tmp->count;
}

/************** about list's special methods **************/
int ctl_list_rmX(ppvoid l, pvoid i1, pvoid i2){
    ListHeader *tmp = getHeader(*l );
    ListNode*    n1 = getNode  (i1);
    ListNode*    n2 = getNode  (i2);
    nodeConnect(n1->prev, n2);
    int ret = 0;
    while(n1 != n2){
        ListNode* ntmp = n1;
        n1 = n1->next;
        ctl_free(ntmp);
        ret++;
    }
    tmp->count -= ret;
    return ret;
}
pvoid ctl_list_copyX(ppvoid l, pvoid i1, pvoid i2, ppvoid out){
    ListHeader* tmp  = getHeader(*l);
    pvoid *p0 = ctl_list_init(NULL, tmp->size, 0);
    ListHeader* tmp2 = getHeader(p0);
    ListNode*     n1 = getNode(i1);
    ListNode*     n2 = getNode(i2);
    while(n1 != n2){
        char *c = tmp2->data.buf;
        ctl_list_addBack(&c, n1->buf);
        n1 = n1->next;
        tmp2->count++;
    }
    if(out != NULL){
        *out = tmp2->data.buf;
    }
    return tmp2->data.buf;
}
pvoid ctl_list_moveX(ppvoid l, pvoid i1, pvoid i2, ppvoid out){
    ListHeader* tmp  = getHeader(*l);
    pvoid *p0 = ctl_list_init(NULL, tmp->size, 0);
    ListHeader* tmp2 = getHeader(p0);
    ListNode*     n1 = getNode(i1);
    ListNode*     n3 = getNode(i2);
    ListNode*     n0 = n1->prev;
    ListNode*     n2 = n3->prev;
    ListNode*     ni;
    for(ni = n1; ni != n3; ni = ni->next){
        tmp ->count--;
        tmp2->count++;
    }
    nodeConnect(&tmp2->data, n1);
    nodeConnect(n2, &tmp2->data);
    nodeConnect(n0, n3);
    if(out != NULL){
        *out = tmp2->data.buf;
    }
    return tmp2->data.buf;
}
int ctl_list_swapX(ppvoid li, pvoid i1, pvoid i2,
                   ppvoid lj, pvoid j1, pvoid j2){
    ListHeader* hi  = getHeader(*li),* hj  = getHeader(*lj);
    ListNode  * ni1 = getNode  ( i1),* nj1 = getNode  ( j1), *i;
    ListNode  * ni3 = getNode  ( i2),* nj3 = getNode  ( j2), *j;
    int count1 = 0, count2 = 0;
    for(i = ni1; i != ni3; i = i->next){
        count1++;
    }
    for(j = nj1; j != nj3; j = j->next){
        count2++;
    }
    ListNode* ni0 = ni1->prev,* ni2 = ni3->prev;
    ListNode* nj0 = nj1->prev,* nj2 = nj3->prev;
    nodeConnect(ni0, nj1);
    nodeConnect(nj0, ni1);
    nodeConnect(ni2, nj3);
    nodeConnect(nj2, ni3);
    hi->size += (count2 - count1);
    hj->size += (count1 - count2);
    return count1;
}

/******************** about iterator **********************/
pvoid ctl_list_iterGetEntryX(pvoid i){
    return i;
}
pvoid ctl_list_iterGetNextX(pvoid i){
    return getNode(i)->next->buf;
}
pvoid ctl_list_iterGetPrevX(pvoid i){
    return getNode(i)->prev->buf;
}

pvoid ctl_list_iterSetEntryX(pvoid i, pcvoid data){
    memcpy(i, data, getNode(i)->head->size);
    return i;
}

pvoid ctl_list_iterDelX(pvoid i){
    ListNode* n = getNode(i);
    nodeConnect(n->prev, n->next);
    n->head->count--;
    return n->next->buf;
}
pvoid ctl_list_iterDelPrevX(pvoid i){
    ctl_list_iterDelX(ppVoid(getNode(i)->prev->buf));
    return i;
}
pvoid ctl_list_iterDelNextX(pvoid i){
    ctl_list_iterDelX(ppVoid(getNode(i)->next->buf));
    return i;
}