/**
 * @file hv_comm_LinkedList.c
 * @brief linked list source code
 *
 * @verbatim
 * ==============================================================================
 *                        ##### How to use #####
 * ==============================================================================
 *
 *
 * @endverbatim
 *
 * @author HiView SoC Software Team
 * @version 1.0.0
 * @date 2023-05-18
 */

#include "hv_comm_LinkedList.h"

VOID Hv_Comm_LinkedList_Init(struct LinkedList *list)
{
    list->next = list;
    list->prev = list;
}

VOID Hv_Comm_LinkedList_Clear(struct LinkedList *list)
{
    struct LinkedList *tmp, *elem;

    Hv_Comm_LinkedList_ForEachSafe (list, tmp, elem) {
        Hv_Comm_LinkedList_Del(NULL, elem);
    }
}

BOOL Hv_Comm_LinkedList_IsEmpty(struct LinkedList *list)
{
    return list->next == list;
}

UINT32 Hv_Comm_LinkedList_Count(struct LinkedList *list)
{
    UINT32 count = 0;
    struct LinkedList *elem;

    Hv_Comm_LinkedList_ForEach (list, elem) {
        count++;
    }

    return count;
}

struct LinkedList *Hv_Comm_LinkedList_Head(struct LinkedList *list)
{
    return list->next != list ? list->next : NULL;
}

struct LinkedList *Hv_Comm_LinkedList_Tail(struct LinkedList *list)
{
    return list->prev != list ? list->prev : NULL;
}

VOID Hv_Comm_LinkedList_AddTail(struct LinkedList *list, struct LinkedList *elem)
{
    struct LinkedList *prev;

    // Delete if exists to prevent adding same item twice
    Hv_Comm_LinkedList_Del(list, elem);

    prev = list->prev;
    list->prev = elem;
    elem->next = list;
    elem->prev = prev;
    prev->next = elem;
}

struct LinkedList *Hv_Comm_LinkedList_PopTail(struct LinkedList *list)
{
    struct LinkedList *tail = list->prev;

    if (Hv_Comm_LinkedList_IsEmpty(list)) {
        return NULL;
    }

    Hv_Comm_LinkedList_Del(list, list->prev);

    return tail;
}

VOID Hv_Comm_LinkedList_AddHead(struct LinkedList *list, struct LinkedList *elem)
{
    struct LinkedList *next;

    // Delete if exists to prevent adding same item twice
    Hv_Comm_LinkedList_Del(list, elem);

    next = list->next;
    list->next = elem;
    elem->prev = list;
    elem->next = next;
    next->prev = elem;
}

struct LinkedList *Hv_Comm_LinkedList_PopHead(struct LinkedList *list)
{
    struct LinkedList *head = list->next;

    if (Hv_Comm_LinkedList_IsEmpty(list)) {
        return NULL;
    }

    Hv_Comm_LinkedList_Del(list, list->next);

    return head;
}

VOID Hv_Comm_LinkedList_AddAfter(struct LinkedList *list, struct LinkedList *prev,
                       struct LinkedList *elem)
{
    (VOID) list;
    struct LinkedList *next;

    // Delete if exists to prevent adding same item twice
    Hv_Comm_LinkedList_Del(list, elem);

    next = prev->next;
    prev->next = elem;
    elem->next = next;
    elem->prev = prev;
    next->prev = elem;
}

VOID Hv_Comm_LinkedList_AddBefore(struct LinkedList *list, struct LinkedList *next,
                        struct LinkedList *elem)
{
    (VOID) list;
    struct LinkedList *prev;

    // Delete if exists to prevent adding same item twice
    Hv_Comm_LinkedList_Del(list, elem);

    prev = next->prev;
    next->prev = elem;
    elem->next = next;
    elem->prev = prev;
    prev->next = elem;
}

VOID Hv_Comm_LinkedList_Del(struct LinkedList *list, struct LinkedList *elem)
{
    (VOID) (list);

    elem->prev->next = elem->next;
    elem->next->prev = elem->prev;
    elem->next = elem;
    elem->prev = elem;
}
