/**
************************************************************************************************
* @文件    : Iec10x.c
* @作者    : 樊春春
* @版本    : V1.0
* @时间    : 2022/07/09 16:18:42
* @邮箱    : fanchcho@gmail.com
* @说明    :
************************************************************************************************
**/

#include "iec104_queue.h"

p_iec104_t          iec104            = NULL;
iec_16u             iec104_state_addr = 0;
iec104_prio_queue_t iec10x_prio_queue_array[IEC10X_PRIO_MAX];

iec_16u      IEC10X_Cp16time2a   = 0;
iec_16u      IEC10X_Cp16time2a_V = 0;
CP56Time2a_T IEC10X_Cp56time2a;

void iec104_lock(void)
{

#ifdef IEC104LOCK
    if (iec104->lock != NULL)
        iec104->lock();
#endif
}

void iec104_unlock(void)
{

#ifdef IEC104LOCK
    if (iec104->unlock != NULL)
        iec104->unlock();
#endif
}

void iec104_queue_init(void)
{
    iec_8u i;
    for (i = 0; i < IEC10X_PRIO_MAX; i++)
    {
        iec104->queue_init(&iec10x_prio_queue_array[i]);
    }
}

void iec104_queue_clear(void)
{
    iec_8u i;
    for (i = 0; i < IEC10X_PRIO_MAX; i++)
    {
        iec104->queue_clear(&iec10x_prio_queue_array[i]);
    }
}

iec_8u iec104_prio_get(iec_8u State)
{

    /*Prio from 0~7*/
    iec_8u Prio = 0;

    return Prio;
}

void iec104_enqueue(iec_8u *enq_buf, iec_16u Length, iec_8u Prio,
                    void (*IEC10XCallBack)(iec104_callback_arg_t *Arg), iec104_callback_arg_t *CallbackArg)
{

    iec104_prio_node_t *new_p;

    iec104_lock();
    if (Length < IEC10X_HEADER_LENGTH)
    {
        LOG("IEC10X_Enqueue,buffer too short \r\n");
        goto END;
    }

    new_p = (iec104_prio_node_t *)iec104->malloc(sizeof(iec104_prio_node_t) + Length - 1);
    if (new_p == NULL)
    {
        LOG("IEC10X_Enqueue,malloc error \r\n");
        goto END;
    }

    memcpy(new_p->value, enq_buf, Length);
    new_p->Length = Length;

    /* Prio from 1~8, Array from 0~7*/
    if (Prio >= IEC10X_PRIO_MAX)
    {
        LOG("IEC104_Enqueue, error Prio(%d) \r\n", Prio);
        goto END;
    }

    /* Set callback Argument */
    new_p->CallBack = IEC10XCallBack;
    if (CallbackArg != NULL)
        new_p->CallBackArg = *CallbackArg;

    new_p->CallBackArg.value = new_p->value;
    LOG("IEC104_Enqueue,Prio(%d) elementNum(%d)len(%d)(%d) \r\n", Prio, iec10x_prio_queue_array[Prio].element_num, Length, new_p->Length);
    // DumpHEX(new_p->value, new_p->Length);
    iec104->enqueue(&iec10x_prio_queue_array[Prio], new_p);
END:
    iec104_unlock();
    return;
}

iec104_prio_node_t *iec104_dequeue(void)
{
    iec_8u              Prio;
    iec104_prio_node_t *ret;
    Prio = iec104->get_prio();
    if (Prio >= 8)
    {
        LOG("IEC104_Dequeue, Error Prio(%d) \r\n", Prio);
        return 0;
    }
    ret = iec104->dequeue(&iec10x_prio_queue_array[Prio]);
    if (ret)
    {
        iec10x_prio_queue_array[Prio].element_num--;
    }
    LOG("IEC104_Dequeue(%d) \r\n", ret->Length);
    return ret;
}
iec104_prio_node_t *IEC104_FindQHead(void)
{

    iec_8u              Prio;
    iec104_prio_node_t *ret;
    Prio = iec104->get_prio();
    if (Prio >= 8)
    {
        return 0;
    }
    ret = iec104->find_qhead(&iec10x_prio_queue_array[Prio]);
    return ret;
}

void iec104_scheduled(int socketfd)
{

    iec104_prio_node_t *de_q_node;

    de_q_node = iec104_dequeue();

    if (de_q_node)
    {

        LOG("<<-------------------IEC10X (%d)-------------------->> \r\n", DeQNode->Length);

        /* call back funtion */
        if (de_q_node->CallBack)
            de_q_node->CallBack(&(de_q_node->CallBackArg));

        dump_hex(DeQNode->value, DeQNode->Length);
        iec104->send(socketfd, (char *)(de_q_node->value), de_q_node->Length);

        iec104_lock();
        iec104->free(de_q_node);
        iec104_unlock();
    }
}

iec_32s iec104_module_register(void *p_iec10x)
{

    int ret;
    if (NULL == p_iec10x)
    {
        return -1;
    }
    else
    {
        iec104 = (p_iec104_t)p_iec10x;
        if (NULL == iec104->init)
        {
            return -1;
        }
        else
        {
            iec104_queue_init();
            // IEC10X_InitInfo();
            ret = iec104->init();
            if (0 == ret)
            {
                LOG("\r\nRegister \"%s\" IEC104 Success, < HuiXing 2014-2015 > ...\r\n", iec10x->name);
            }
            return ret;
        }
    }
}

#ifdef PRIO_QUEUE

/*******************************************************************************
* Function Name  : iec10x_prio_highest
* Description    :
* Input          : None
* Output         : None
* Return         : RET_ERROR failure
                   len sucess
*******************************************************************************/
iec_8u iec10x_prio_highest(void)
{

    int    i, prio;
    iec_8u flag = 0;
    prio        = -1;
    // LOG("%s \r\n",__FUNCTION__);
    for (i = 0; i < IEC10X_PRIO_MAX; i++)
    {
        if (iec10x_prio_queue_array[i].element_num > 0 && !flag)
        {
            // return i;
            prio = i;
            flag = 1;
            LOG("<%s>Prio[%d],Num%d. \n", __FUNCTION__, i, iec10x_prio_queue_array[i].element_num);
        }
        LOG("[%d]%d.", i, iec10x_prio_queue_array[i].element_num);
    }
    LOG("out(%d)\r\n", prio);
    return prio;
}

void iec104_prio_queue_init(iec104_prio_queue_t *PrioQueue)
{

    PrioQueue->header      = NULL;
    PrioQueue->tail        = NULL;
    PrioQueue->element_num = 0;
}
iec_8u iec104_prio_enqueue(iec104_prio_queue_t *queue_hdr, iec104_prio_node_t *new_p)
{

    new_p->Next = NULL;

    if (queue_hdr->header == NULL)
    {
        queue_hdr->header = new_p;
        queue_hdr->tail   = new_p;
    }
    else
    {
        queue_hdr->tail->Next = new_p;
        queue_hdr->tail       = new_p;
    }

    queue_hdr->element_num++;
    LOG("%s ElementNum(%d) \r\n", __FUNCTION__, queue_hdr->element_num);
    return RET_SUCESS;
}

iec104_prio_node_t *iec104_prio_dequeue(iec104_prio_queue_t *queue_hdr)
{

    iec104_prio_node_t *PrioNode_DeQ;
    if (queue_hdr->header == NULL)
    {
        LOG("PrioDeQueue,error \r\n");
        return NULL;
    }
    PrioNode_DeQ = queue_hdr->header;

    queue_hdr->header = queue_hdr->header->Next;
    if (queue_hdr->header == NULL)
    {
        queue_hdr->tail = NULL;
    }

    LOG("PrioDeQueue(%d) \r\n", PrioNode_DeQ->Length);

    return PrioNode_DeQ;
}

iec104_prio_node_t *iec104_prio_queue_find_head(iec104_prio_queue_t *queue_hdr)
{

    iec104_prio_node_t *PrioNode_DeQ;
    if (queue_hdr->header == NULL)
    {
        // LOG("PrioDeQueue,error \r\n");
        return NULL;
    }
    PrioNode_DeQ = queue_hdr->header;

    // LOG("PrioDeQueue(%d) \r\n",PrioNode_DeQ->Length);

    return PrioNode_DeQ;
}

void *IEC10X_PeekQueue(iec104_prio_queue_t *queue_hdr)
{

    if (queue_hdr->header == NULL)
    {
        // LOG("   ");
        return NULL;
    }
    return queue_hdr->header->value;
}

int IEC10X_Prio_IsEmptyQueue(iec104_prio_queue_t *queue_hdr)
{

    if (queue_hdr->header == NULL)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}

void iec104_prio_queue_clear(iec104_prio_queue_t *iec10x_prio_queue)
{

    iec104_prio_node_t *p = iec10x_prio_queue->header;
    while (p != NULL)
    {
        iec10x_prio_queue->header = iec10x_prio_queue->header->Next;
        iec104->free(p);
        p = iec10x_prio_queue->header;
    }
    iec10x_prio_queue->tail        = NULL;
    iec10x_prio_queue->element_num = 0;
    return;
}

#endif