/* -------------------------------------------------------------------------
 *  This file is part of the oGRAC project.
 * Copyright (c) 2024 Huawei Technologies Co.,Ltd.
 *
 * oGRAC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * cm_bilist.c
 *
 *
 * IDENTIFICATION
 * src/common/cm_bilist.c
 *
 * -------------------------------------------------------------------------
 */
#include "cm_common_module.h"
#include "cm_bilist.h"

void cm_bilist_del_tail(bilist_t *bilist)
{
    (void)cm_bilist_remove_tail(bilist);
}

bilist_node_t* cm_bilist_remove_tail(bilist_t *bilist)
{
    if (bilist->count == 0) {
        return NULL;
    }

    bilist_node_t *tail = bilist->tail;

    if (bilist->head != bilist->tail) {
        bilist->tail = bilist->tail->prev;
        bilist->tail->next = NULL;
    } else {
        bilist->head = NULL;
        bilist->tail = NULL;
    }
    bilist->count--;
    tail->next = NULL;
    tail->prev = NULL;

    return tail;
}

void cm_bilist_del_head(bilist_t *bilist)
{
    (void)cm_bilist_remove_head(bilist);
}

bilist_node_t* cm_bilist_remove_head(bilist_t *bilist)
{
    if (bilist->count == 0) {
        return NULL;
    }

    bilist_node_t *head = bilist->head;

    if (bilist->head != bilist->tail) {
        bilist->head = bilist->head->next;
        bilist->head->prev = NULL;
    } else {
        bilist->head = NULL;
        bilist->tail = NULL;
    }
    bilist->count--;
    head->next = NULL;
    head->prev = NULL;

    return head;
}

void cm_bilist_insert_check(bilist_t *bilist, bilist_node_t *node)
{
    bilist_node_t *tmp_node = bilist->head;

    while (tmp_node != NULL) {
        if (tmp_node == node) {
            CM_NEVER;
        }
        tmp_node = BINODE_NEXT(tmp_node);
    }
}

void cm_bilist_delete_check(bilist_t *bilist, bilist_node_t *node)
{
    bilist_node_t *tmp_node = bilist->head;
    uint32 node_cnt = 0;

    while (tmp_node != NULL) {
        if (tmp_node == node) {
            node_cnt++;
        }
        tmp_node = BINODE_NEXT(tmp_node);
    }

    CM_ASSERT(node_cnt == 1);
}

void cm_bilist_del(bilist_node_t *node, bilist_t *bilist)
{
#ifdef _DEBUG
    cm_bilist_delete_check(bilist, node);
#endif

    if (node == bilist->head) {
        cm_bilist_del_head(bilist);
        return;
    }

    if (node == bilist->tail) {
        cm_bilist_del_tail(bilist);
        return;
    }

    if (node->prev == NULL || node->next == NULL) {
        return;
    }
    CM_ASSERT(bilist->count > 0);
    node->prev->next = node->next;
    node->next->prev = node->prev;
    node->prev = NULL;
    node->next = NULL;
    bilist->count--;
}

void cm_bilist_add_tail(bilist_node_t *node, bilist_t *bilist)
{
#ifdef _DEBUG
    cm_bilist_insert_check(bilist, node);
#endif

    if (bilist->tail != NULL) {
        node->prev = bilist->tail;
        node->next = NULL;
        bilist->tail->next = node;
        bilist->tail = node;
    } else {
        node->next = NULL;
        node->prev = NULL;
        bilist->head = node;
        bilist->tail = node;
    }
    bilist->count++;
}

void cm_bilist_add_head(bilist_node_t *node, bilist_t *bilist)
{
#ifdef _DEBUG
    cm_bilist_insert_check(bilist, node);
#endif

    if (bilist->head != NULL) {
        node->next = bilist->head;
        node->prev = NULL;
        bilist->head->prev = node;
        bilist->head = node;
    } else {
        node->next = NULL;
        node->prev = NULL;
        bilist->head = node;
        bilist->tail = node;
    }
    bilist->count++;
}

void cm_bilist_add_prev(bilist_node_t *node, bilist_node_t *where, bilist_t *bilist)
{
#ifdef _DEBUG
    cm_bilist_insert_check(bilist, node);
#endif

    if (where == bilist->head) {
        cm_bilist_add_head(node, bilist);
        return;
    }
    node->prev = where->prev;
    node->next = where;
    where->prev = node;
    node->prev->next = node;
    bilist->count++;
}

void cm_bilist_add_next(bilist_node_t *node, bilist_node_t *where, bilist_t *bilist)
{
#ifdef _DEBUG
    cm_bilist_insert_check(bilist, node);
#endif

    if (where == bilist->tail) {
        cm_bilist_add_tail(node, bilist);
        return;
    }
    node->next = where->next;
    node->prev = where;
    where->next = node;
    node->next->prev = node;
    bilist->count++;
}

bilist_node_t *cm_bilist_get(bilist_t *bilist, uint32 index)
{
    bilist_node_t *node = NULL;

    if (index >= bilist->count) {
        return NULL;
    }

    node = bilist->head;
    for (uint32 i = 0; i < index; i++) {
        node = BINODE_NEXT(node);
    }

    return node;
}