/*********************************************************************************************************************
* TC264 Opensourec Library TC264 Դ⣩һڹٷ SDK ӿڵĵԴ
* Copyright (c) 2022 SEEKFREE ɿƼ
*
* ļ TC264 Դһ
*
* TC264 Դ 
* Ըᷢ GPLGNU General Public License GNUͨù֤
*  GPL ĵ3棨 GPL3.0ѡģκκİ汾·/޸
*
* Դķϣܷãδκεı֤
* ûԻʺض;ı֤
* ϸμ GPL
*
* ӦյԴͬʱյһ GPL ĸ
* ûУ<https://www.gnu.org/licenses/>
*
* ע
* Դʹ GPL3.0 Դ֤Э Ϊİ汾
* Ӣİ libraries/doc ļµ GPL3_permission_statement.txt ļ
* ֤ libraries ļ ļµ LICENSE ļ
* ӭλʹò ޸ʱ뱣ɿƼİȨ
*
* ļ          zf_common_fifo
* ˾          ɶɿƼ޹˾
* 汾Ϣ          鿴 libraries/doc ļ version ļ 汾˵
*           ADS v1.9.4
* ƽ̨          TC264D
*           https://seekfree.taobao.com/
*
* ޸ļ¼
*                               ע
* 2022-09-15       pudding            first version
********************************************************************************************************************/

#include "zf_common_fifo.h"

//-------------------------------------------------------------------------------------------------------------------
//      FIFO ͷָλ
// ˵     *fifo               FIFO ָ
// ˵     offset              ƫ
// ز     void
// ʹʾ     fifo_head_offset(fifo, 1);
// עϢ     ļڲ ûùע Ҳ޸
//-------------------------------------------------------------------------------------------------------------------
static void fifo_head_offset (fifo_struct *fifo, uint32 offset)
{
    fifo->head += offset;
    
    while(fifo->max <= fifo->head)                                              // ΧС ֱС󻺳С
    {
        fifo->head -= fifo->max;
    }
}

//-------------------------------------------------------------------------------------------------------------------
//      FIFO βָλ
// ˵     *fifo               FIFO ָ
// ˵     offset              ƫ
// ز     void
// ʹʾ     fifo_end_offset(fifo, 1);
// עϢ     ļڲ ûùע Ҳ޸
//-------------------------------------------------------------------------------------------------------------------
static void fifo_end_offset (fifo_struct *fifo, uint32 offset)
{
    fifo->end += offset;
    
    while(fifo->max <= fifo->end)                                               // ΧС ֱС󻺳С
    {
        fifo->end -= fifo->max;
    }
}

//-------------------------------------------------------------------------------------------------------------------
//      FIFO û
// ˵     *fifo               FIFO ָ
// ز     void
// ʹʾ     fifo_clear(fifo);
// עϢ     յǰ FIFO ڴ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_clear (fifo_struct *fifo)
{
    fifo_state_enum return_state = FIFO_SUCCESS;
    do
    {
        if(FIFO_CLEAR & fifo->execution)
        {
            return_state = FIFO_CLEAR_UNDO;
            break;
        }
        fifo->execution |= FIFO_CLEAR;
        fifo->head      = 0;
        fifo->end       = 0;
        fifo->size      = fifo->max;

        switch(fifo->type)
        {
            case FIFO_DATA_8BIT:
                memset(fifo->buffer, 0, fifo->max);
                break;
            case FIFO_DATA_16BIT:
                memset(fifo->buffer, 0, fifo->max * 2);
                break;
            case FIFO_DATA_32BIT:
                memset(fifo->buffer, 0, fifo->max * 4);
                break;
        }
        fifo->execution &= ~FIFO_CLEAR;
    }while(0);
    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//      FIFO ѯǰݸ
// ˵     *fifo               FIFO ָ
// ز     uint32              ʹó
// ʹʾ     uint32 len = fifo_used(fifo);
// עϢ
//-------------------------------------------------------------------------------------------------------------------
uint32 fifo_used (fifo_struct *fifo)
{
    return (fifo->max - fifo->size);
}

//-------------------------------------------------------------------------------------------------------------------
//       FIFO д
// ˵     *fifo               FIFO ָ
// ˵     dat                 
// ز     fifo_state_enum     ״̬
// ʹʾ     zf_log(fifo_write_element(&fifo, data) == FIFO_SUCCESS, "fifo_write_byte error");
// עϢ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_write_element (fifo_struct *fifo, uint32 dat)
{
    fifo_state_enum return_state = FIFO_SUCCESS;

    do
    {
        if(FIFO_WRITE & fifo->execution)
        {
            return_state = FIFO_WRITE_UNDO;
            break;
        }
        fifo->execution |= FIFO_WRITE;

        if(1 <= fifo->size)                                                     // ʣռ㹻װ±
        {
            switch(fifo->type)
            {
                case FIFO_DATA_8BIT:
                    ((uint8 *)fifo->buffer)[fifo->head] = dat & 0xFF;
                    break;
                case FIFO_DATA_16BIT:
                    ((uint16 *)fifo->buffer)[fifo->head] = dat & 0xFFFF;
                    break;
                case FIFO_DATA_32BIT:
                    ((uint32 *)fifo->buffer)[fifo->head] = dat;
                    break;
            }
            fifo_head_offset(fifo, 1);                                          // ͷָƫ
            fifo->size -= 1;                                                    // ʣ೤ȼС
        }
        else
        {
            return_state = FIFO_SPACE_NO_ENOUGH;
        }
        fifo->execution &= ~FIFO_WRITE;
    }while(0);

    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//       FIFO д
// ˵     *fifo               FIFO ָ
// ˵     *dat                Դָ
// ˵     length              Ҫдݳ
// ز     fifo_state_enum     ״̬
// ʹʾ     zf_log(fifo_write_buffer(&fifo, data, 32) == FIFO_SUCCESS, "fifo_write_buffer error");
// עϢ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_write_buffer (fifo_struct *fifo, void *dat, uint32 length)
{
    fifo_state_enum return_state = FIFO_SUCCESS;
    uint32 temp_length = 0;
    
    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
            break;
        }
        if(FIFO_WRITE & fifo->execution)
        {
            return_state = FIFO_WRITE_UNDO;
            break;
        }
        fifo->execution |= FIFO_WRITE;

        if(length <= fifo->size)                                                 // ʣռ㹻װ±
        {
            temp_length = fifo->max - fifo->head;                               // ͷָ뻺βжٿռ

            if(length > temp_length)                                            // 뻺βȲд λֶβ
            {
                switch(fifo->type)
                {
                    case FIFO_DATA_8BIT:
                        memcpy(
                            &(((uint8 *)fifo->buffer)[fifo->head]),
                            dat, temp_length);                                  // һ
                        fifo_head_offset(fifo, temp_length);                    // ͷָƫ
                        memcpy(
                            &(((uint8 *)fifo->buffer)[fifo->head]),
                            &(((uint8 *)dat)[temp_length]),
                            length - temp_length);                              // ڶ
                        fifo_head_offset(fifo, length - temp_length);           // ͷָƫ
                        break;
                    case FIFO_DATA_16BIT:
                        memcpy(
                            &(((uint16 *)fifo->buffer)[fifo->head]),
                            dat, temp_length * 2);                              // һ
                        fifo_head_offset(fifo, temp_length);                    // ͷָƫ
                        memcpy(
                            &(((uint16 *)fifo->buffer)[fifo->head]),
                            &(((uint16 *)dat)[temp_length]),
                            (length - temp_length) * 2);                        // ڶ
                        fifo_head_offset(fifo, length - temp_length);           // ͷָƫ
                        break;
                    case FIFO_DATA_32BIT:
                        memcpy(
                            &(((uint32 *)fifo->buffer)[fifo->head]),
                            dat, temp_length * 4);                              // һ
                        fifo_head_offset(fifo, temp_length);                    // ͷָƫ
                        memcpy(
                            &(((uint32 *)fifo->buffer)[fifo->head]),
                            &(((uint32 *)dat)[temp_length]),
                            (length - temp_length) * 4);                        // ڶ
                        fifo_head_offset(fifo, length - temp_length);           // ͷָƫ
                        break;
                }
            }
            else
            {
                switch(fifo->type)
                {
                    case FIFO_DATA_8BIT:
                        memcpy(
                            &(((uint8 *)fifo->buffer)[fifo->head]),
                            dat, length);                                       // һд
                        fifo_head_offset(fifo, length);                         // ͷָƫ
                        break;
                    case FIFO_DATA_16BIT:
                        memcpy(
                            &(((uint16 *)fifo->buffer)[fifo->head]),
                            dat, length * 2);                                   // һд
                        fifo_head_offset(fifo, length);                         // ͷָƫ
                        break;
                    case FIFO_DATA_32BIT:
                        memcpy(
                            &(((uint32 *)fifo->buffer)[fifo->head]),
                            dat, length * 4);                                   // һд
                        fifo_head_offset(fifo, length);                         // ͷָƫ
                        break;
                }
//                memcpy(&fifo->buffer[fifo->head], dat, length);                 // һд
//                fifo_head_offset(fifo, length);                                 // ͷָƫ
            }

            fifo->size -= length;                                               // ʣ೤ȼС
        }
        else
        {
            return_state = FIFO_SPACE_NO_ENOUGH;
        }
        fifo->execution &= ~FIFO_WRITE;
    }while(0);

    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//       FIFO ȡ
// ˵     *fifo               FIFO ָ
// ˵     *dat                Ŀ껺ָ
// ˵     flag                Ƿ FIFO ״̬ ѡǷնȡ
// ز     fifo_state_enum     ״̬
// ʹʾ     zf_log(fifo_read_element(&fifo, data, FIFO_READ_ONLY) == FIFO_SUCCESS, "fifo_read_byte error");
// עϢ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_read_element (fifo_struct *fifo, void *dat, fifo_operation_enum flag)
{
    fifo_state_enum return_state = FIFO_SUCCESS;

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
            break;
        }
        fifo->execution |= FIFO_READ;

        if(1 > fifo_used(fifo))
        {
            return_state = FIFO_DATA_NO_ENOUGH;                                 // ־ݲ
        }

        switch(fifo->type)
        {
            case FIFO_DATA_8BIT:
                *((uint8 *)dat) = ((uint8 *)fifo->buffer)[fifo->end];
                break;
            case FIFO_DATA_16BIT:
                *((uint16 *)dat) = ((uint16 *)fifo->buffer)[fifo->end];
                break;
            case FIFO_DATA_32BIT:
                *((uint32 *)dat) = ((uint32 *)fifo->buffer)[fifo->end];
                break;
        }

        if(flag == FIFO_READ_AND_CLEAN)                                         // ѡȡ FIFO ״̬
        {
            if(FIFO_CLEAR & fifo->execution)
            {
                return_state = FIFO_CLEAR_UNDO;
                break;
            }
            fifo->execution |= FIFO_CLEAR;
            fifo_end_offset(fifo, 1);                                           // ƶ FIFO ͷָ
            fifo->size += 1;
            fifo->execution &= ~FIFO_CLEAR;
        }
    }while(0);
    fifo->execution &= FIFO_READ;

    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//       FIFO ȡ
// ˵     *fifo               FIFO ָ
// ˵     *dat                Ŀ껺ָ
// ˵     *length             ȡݳ ûôᱻ޸
// ˵     flag                Ƿ FIFO ״̬ ѡǷնȡ
// ز     fifo_state_enum     ״̬
// ʹʾ     zf_log(fifo_read_buffer(&fifo, data, &length, FIFO_READ_ONLY) == FIFO_SUCCESS, "fifo_read_buffer error");
// עϢ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_read_buffer (fifo_struct *fifo, void *dat, uint32 *length, fifo_operation_enum flag)
{
    fifo_state_enum return_state = FIFO_SUCCESS;
    uint32 temp_length;

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
            break;
        }
        fifo->execution |= FIFO_READ;

        if(*length > fifo_used(fifo))
        {
            *length = (fifo->max - fifo->size);                                 // ȡĳ
            return_state = FIFO_DATA_NO_ENOUGH;                                 // ־ݲ
        }

        temp_length = fifo->max - fifo->end;                                    // βָ뻺βжٿռ
        if(*length <= temp_length)                                              // 㹻һԶȡ
        {
            switch(fifo->type)
            {
                case FIFO_DATA_8BIT:
                    memcpy(dat, &(((uint8 *)fifo->buffer)[fifo->end]), *length);
                    break;
                case FIFO_DATA_16BIT:
                    memcpy(dat, &(((uint16 *)fifo->buffer)[fifo->end]), *length * 2);
                    break;
                case FIFO_DATA_32BIT:
                    memcpy(dat, &(((uint32 *)fifo->buffer)[fifo->end]), *length * 4);
                    break;
            }
        }
        else
        {
            switch(fifo->type)
            {
                case FIFO_DATA_8BIT:
                    memcpy(dat, &(((uint8 *)fifo->buffer)[fifo->end]), temp_length);
                    memcpy(&(((uint8 *)dat)[temp_length]), fifo->buffer, *length - temp_length);
                    break;
                case FIFO_DATA_16BIT:
                    memcpy(dat, &(((uint16 *)fifo->buffer)[fifo->end]), temp_length * 2);
                    memcpy(&(((uint16 *)dat)[temp_length]), fifo->buffer, (*length - temp_length) * 2);
                    break;
                case FIFO_DATA_32BIT:
                    memcpy(dat, &(((uint32 *)fifo->buffer)[fifo->end]), temp_length * 4);
                    memcpy(&(((uint32 *)dat)[temp_length]), fifo->buffer, (*length - temp_length) * 4);
                    break;
            }
        }

        if(flag == FIFO_READ_AND_CLEAN)                                         // ѡȡ FIFO ״̬
        {
            if(FIFO_CLEAR & fifo->execution)
            {
                return_state = FIFO_CLEAR_UNDO;
                break;
            }
            fifo->execution |= FIFO_CLEAR;
            fifo_end_offset(fifo, *length);                                     // ƶ FIFO ͷָ
            fifo->size += *length;
            fifo->execution &= ~FIFO_CLEAR;
        }
    }while(0);
    fifo->execution &= FIFO_READ;

    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//       FIFO βȡָ buffer
// ˵     *fifo               FIFO ָ
// ˵     *dat                Ŀ껺ָ
// ˵     *length             ȡݳ ûôᱻ޸
// ˵     flag                Ƿ FIFO ״̬ ѡǷնȡ
// ز     fifo_state_enum     ״̬
// ʹʾ     zf_log(fifo_read_tail_buffer(&fifo, data, &length, FIFO_READ_ONLY) == FIFO_SUCCESS, "fifo_read_buffer error");
// עϢ     ʹ FIFO_READ_AND_CLEAN  ᶪݲ FIFO
//              ʹ FIFO_READ_AND_CLEAN  ᶪݲ FIFO
//              ʹ FIFO_READ_AND_CLEAN  ᶪݲ FIFO
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_read_tail_buffer (fifo_struct *fifo, void *dat, uint32 *length, fifo_operation_enum flag)
{
    fifo_state_enum return_state = FIFO_SUCCESS;
    uint32 temp_length;

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
            break;
        }
        fifo->execution |= FIFO_READ;

        if(*length > fifo_used(fifo))
        {
            *length = (fifo->max - fifo->size);                                 // ȡĳ
            return_state = FIFO_DATA_NO_ENOUGH;                                 // ־ݲ
        }

        if((fifo->head > fifo->end) || (fifo->head >= *length))
        {
            switch(fifo->type)
            {
                case FIFO_DATA_8BIT:
                    memcpy(dat, &(((uint8 *)fifo->buffer)[fifo->head - *length]), *length);
                    break;
                case FIFO_DATA_16BIT:
                    memcpy(dat, &(((uint16 *)fifo->buffer)[fifo->head - *length]), *length * 2);
                    break;
                case FIFO_DATA_32BIT:
                    memcpy(dat, &(((uint32 *)fifo->buffer)[fifo->head - *length]), *length * 4);
                    break;
            }
        }
        else
        {
            temp_length = *length - fifo->head;                                 // βָ뻺βжٿռ
            switch(fifo->type)
            {
                case FIFO_DATA_8BIT:
                    memcpy(dat, &(((uint8 *)fifo->buffer)[fifo->max - temp_length]), temp_length);
                    memcpy(&(((uint8 *)dat)[temp_length]), &(((uint8 *)fifo->buffer)[fifo->head - *length]), (*length - temp_length));
                    break;
                case FIFO_DATA_16BIT:
                    memcpy(dat, &(((uint16 *)fifo->buffer)[fifo->max - temp_length]), temp_length * 2);
                    memcpy(&(((uint16 *)dat)[temp_length]), &(((uint16 *)fifo->buffer)[fifo->head - *length]), (*length - temp_length) * 2);
                    break;
                case FIFO_DATA_32BIT:
                    memcpy(dat, &(((uint32 *)fifo->buffer)[fifo->max - temp_length]), temp_length * 4);
                    memcpy(&(((uint32 *)dat)[temp_length]), &(((uint32 *)fifo->buffer)[fifo->head - *length]), (*length - temp_length) * 4);
                    break;
            }
        }

        if(flag == FIFO_READ_AND_CLEAN)                                         // ѡȡ FIFO ״̬
        {
            if(FIFO_CLEAR & fifo->execution)
            {
                return_state = FIFO_CLEAR_UNDO;
                break;
            }
            fifo->execution |= FIFO_CLEAR;
            fifo_end_offset(fifo, (fifo->max - fifo->size));
            fifo->size = fifo->max;
            fifo->execution &= ~FIFO_CLEAR;
        }
    }while(0);
    fifo->execution &= FIFO_READ;

    return return_state;
}

//-------------------------------------------------------------------------------------------------------------------
//      FIFO ʼ ضӦ
// ˵     *fifo               FIFO ָ
// ˵     type                FIFO λ
// ˵     *buffer_addr        ҪصĻ
// ˵     size                С
// ز     fifo_state_enum     ״̬
// ʹʾ     fifo_init(&user_fifo, user_buffer, 64);
// עϢ
//-------------------------------------------------------------------------------------------------------------------
fifo_state_enum fifo_init (fifo_struct *fifo, fifo_data_type_enum type, void *buffer_addr, uint32 size)
{
    fifo_state_enum return_value = FIFO_SUCCESS;
    do
    {
        if(NULL == buffer_addr)
        {
            return_value = FIFO_BUFFER_NULL;
            break;
        }
        fifo->buffer            = buffer_addr;
        fifo->execution         = FIFO_IDLE;
        fifo->type              = type;
        fifo->head              = 0;
        fifo->end               = 0;
        fifo->size              = size;
        fifo->max               = size;
    }while(0);
    return return_value;
}
