//----------------------------------------------------------------------------//
//                           GNU GPL OS/K                                     //
//                                                                            //
//  Desc:       Doubly linked lists implementation                            //
//                                                                            //
//                                                                            //
//  Copyright © 2018-2019 The OS/K Team                                       //
//                                                                            //
//  This file is part of OS/K.                                                //
//                                                                            //
//  OS/K is free software: you can redistribute it and/or modify              //
//  it under the terms of the GNU General Public License as published by      //
//  the Free Software Foundation, either version 3 of the License, or         //
//  any later version.                                                        //
//                                                                            //
//  OS/K 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 General Public License for more details.                              //
//                                                                            //
//  You should have received a copy of the GNU General Public License         //
//  along with OS/K.  If not, see <https://www.gnu.org/licenses/>.            //
//----------------------------------------------------------------------------//

#ifndef _LIBC_H
#include <libc.h>
#endif

#ifndef _LIB_LIST_H
#define _LIB_LIST_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ListHead_t       ListHead_t;
typedef struct ListNode_t       ListNode_t;

//------------------------------------------//

struct ListHead_t
{
    ulong               length;
    ListNode_t         *first;
    ListNode_t         *last;
};

struct ListNode_t
{
    ListHead_t         *head;
    ListNode_t         *prev;
    ListNode_t         *next;
};

//------------------------------------------//

//
// Create a list head with an extern lock
//
static inline
ListHead_t *ExCreateListHead()
{
    ListHead_t *head = malloc(sizeof(ListHead_t));

    if (head == NULL) return NULL;

    head->first = head->last = NULL;
    head->length = 0;

    return head;
}

static inline
ListNode_t *ExCreateNode()
{
    ListNode_t *node = malloc(sizeof(ListNode_t));

    if (node == NULL) return NULL;

    node->prev = node->next = NULL;
    node->head = NULL;

    return node;
}

//
// Prepend node at beginning of list
//
static inline
ListHead_t *ExPrependNode(ListHead_t *head, ListNode_t *node)
{
    assert(head && node);

    node->head = head;
    node->prev = NULL;

    if (head->length > 0) {
        node->next = head->first;
        head->first->prev = node;
        head->first = node;
    }

    else {
        head->first = node;
        head->last  = node;
        node->next  = NULL;
    }

    head->length++;

    return head;
}

//
// Append node at end of list
//
static inline
ListHead_t *ExAppendNode(ListHead_t *head, ListNode_t *node)
{
    assert(head && node);

    node->head = head;
    node->next = NULL;

    if (head->length > 0) {
        node->prev = head->last;
        head->last->next = node;
        head->last = node;
    }

    else {
        head->first = node;
        head->last  = node;
        node->prev  = NULL;
    }

    head->length++;

    return head;
}

//
// Insert node2 before node1
//
static inline ListHead_t
*ExAddNodeBefore(ListHead_t *head, ListNode_t *node1, ListNode_t *node2)
{
    assert(head && node1 && node2 && node1->head == head);

    if (head->first == node1) {
        return ExPrependNode(head, node2);
    }

    node2->head = head;
    node2->next = node1;
    node2->prev = node1->prev;

    // node1->prev does exist
    // or node1 would be first
    node1->prev->next = node2;
    node1->prev = node2;

    head->length++;

    return head;
}

//
// Insert node2 after node1
//
static inline ListHead_t
*ExAddNodeAfter(ListHead_t *head, ListNode_t *node1, ListNode_t *node2)
{
    assert(head && node1 && node2 && node1->head == head);

    if (head->last == node1) {
        return ExAppendNode(head, node2);
    }

    node2->head = head;
    node2->prev = node1;
    node2->next = node1->next;

    node1->next->prev = node2;
    node1->next = node2;

    head->length++;

    return head;
}

//
// Remove node of list (and frees it)
//
static inline ListHead_t
*ExRemoveNode(ListHead_t *head, ListNode_t *node)
{
    assert(head);
    assert(node);
    assert(head->length > 0);
    assert(head && node && head->length > 0 && node->head == head);

    if (head->length == 1) {
        head->first = head->last = NULL;
        goto leave;
    }

    if (head->first == node) {
        head->first = node->next;
        node->next->prev = NULL;
    }

    else if (head->last == node) {
        head->last = node->prev;
        node->prev->next = NULL;
    }

    else {
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

leave:
    head->length--;
    free(node);

    return head;
}

//
// Free a list head
//
static inline void
ExDestroyListHead(ListHead_t *head)
{
    assert(head);
    free(head);
}

#define ExGetNodeData(list, type) ((type)list)

//------------------------------------------//

#ifdef __cplusplus
}
#endif

#endif