/*********************************************************************************************************************
* RT1064DVL6A Opensourec Library RT1064DVL6A Դ⣩һڹٷ SDK ӿڵĵԴ
* Copyright (c) 2022 SEEKFREE ɿƼ
* 
* ļ RT1064DVL6A Դһ
* 
* RT1064DVL6A Դ 
* Ըᷢ 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 ļ 汾˵
*           IAR 8.32.4 or MDK 5.33
* ƽ̨          RT1064DVL6A
*           https://seekfree.taobao.com/
* 
* ޸ļ¼
*                               ע
* 2022-09-21        SeekFree            first version
********************************************************************************************************************/

#include "zf_common_debug.h"
#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)
{
    zf_assert(NULL != fifo);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ
    do
    {
//        if(FIFO_IDLE != fifo->execution)                                        // жǷǰ FIFO Ƿ
//        {
//            return_state = FIFO_RESET_UNDO;                                     // òδ
//            break;
//        }
        fifo->execution |= FIFO_RESET;                                          // òλ
        fifo->head      = 0;                                                    //  FIFO ֵλ
        fifo->end       = 0;                                                    //  FIFO ֵλ
        fifo->size      = fifo->max;                                            //  FIFO ֵλ
        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_IDLE;                                            // ״̬λ
    }while(0);
    return return_state;
}

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

//-------------------------------------------------------------------------------------------------------------------
//       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)
{
    zf_assert(NULL != fifo);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ

    do
    {
        if((FIFO_RESET | 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;  break;
                case FIFO_DATA_16BIT:   ((uint16 *)fifo->buffer)[fifo->head] = dat; 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  д ؿռ䲻
        }
        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)
{
    zf_assert(NULL != fifo);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ
    uint32 temp_length = 0;
    
    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;                                    // û쳣
            break;
        }
        if((FIFO_RESET | 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;
                }
            }

            fifo->size -= length;                                               // ʣ೤ȼС
        }
        else
        {
            return_state = FIFO_SPACE_NO_ENOUGH;                                // ǰ FIFO  д ؿռ䲻
        }
        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)
{
    zf_assert(NULL != fifo);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;                                    // û쳣
        }
        else
        {
            if((FIFO_RESET | FIFO_CLEAR) & fifo->execution)                     // жǷǰ FIFO Ƿִջò
            {
                return_state = FIFO_READ_UNDO;                                  // ȡδ
                break;
            }

            if(1 > fifo_used(fifo))
            {
                return_state = FIFO_DATA_NO_ENOUGH;                             // û ݳȲ
                break;                                                          // ֱ˳
            }

            fifo->execution |= FIFO_READ;                                       // λ
            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;
            }
            fifo->execution &= ~FIFO_READ;                                      // λ
        }
        
        if(FIFO_READ_AND_CLEAN == flag)                                         // ѡȡ FIFO ״̬
        {
            if((FIFO_RESET | FIFO_CLEAR | FIFO_READ) == 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);

    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)
{
    zf_assert(NULL != fifo);
    zf_assert(NULL != length);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ
    uint32 temp_length = 0;
    uint32 fifo_data_length = 0;

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
        }
        else
        {
            if((FIFO_RESET | FIFO_CLEAR) & fifo->execution)                     // жǷǰ FIFO Ƿִջò
            {
                *length = fifo_data_length;                                     // ȡĳ
                return_state = FIFO_READ_UNDO;                                  // ȡδ
                break;
            }

            fifo_data_length = fifo_used(fifo);                                 // ȡǰж
            if(*length > fifo_data_length)                                      // жϳǷ㹻
            {
                *length = fifo_data_length;                                     // ȡĳ
                return_state = FIFO_DATA_NO_ENOUGH;                             // ־ݲ
                if(0 == fifo_data_length)                                       // û ֱ˳
                {
                    fifo->execution &= ~FIFO_READ;                              // λ
                    break;
                }
            }

            fifo->execution |= FIFO_READ;                                       // λ
            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;
                }
            }
            fifo->execution &= ~FIFO_READ;                                      // λ
        }
        
        if(FIFO_READ_AND_CLEAN == flag)                                         // ѡȡ FIFO ״̬
        {
            if((FIFO_RESET | FIFO_CLEAR | FIFO_READ) == 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);

    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)
{
    zf_assert(NULL != fifo);
    zf_assert(NULL != length);
    fifo_state_enum return_state = FIFO_SUCCESS;                                // ֵ
    uint32 temp_length = 0;
    uint32 fifo_data_length = 0;

    do
    {
        if(NULL == dat)
        {
            return_state = FIFO_BUFFER_NULL;
        }
        else
        {
            if((FIFO_RESET | FIFO_CLEAR | FIFO_WRITE) & fifo->execution)        // жǷǰ FIFO Ƿִջò
            {
                *length = fifo_data_length;                                     // ȡĳ
                return_state = FIFO_READ_UNDO;                                  // ȡδ
                break;
            }

            fifo_data_length = fifo_used(fifo);                                 // ȡǰж
            if(*length > fifo_data_length)                                      // жϳǷ㹻
            {
                *length = fifo_data_length;                                     // ȡĳ
                return_state = FIFO_DATA_NO_ENOUGH;                             // ־ݲ
                if(0 == fifo_data_length)                                       // û ֱ˳
                {
                    fifo->execution &= ~FIFO_READ;                              // λ
                    break;
                }
            }

            fifo->execution |= FIFO_READ;                                       // λ
            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;
                }
            }
            fifo->execution &= ~FIFO_READ;                                      // λ
        }
        
        if(FIFO_READ_AND_CLEAN == flag)                                         // ѡȡ FIFO ״̬
        {
            if((FIFO_RESET | FIFO_CLEAR | FIFO_READ) == fifo->execution)        //    ȡ ״̬ 쳣
            {
                return_state = FIFO_CLEAR_UNDO;                                 // ղδ
                break;
            }
            fifo_clear(fifo);
        }
    }while(0);

    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)
{
    zf_assert(NULL != fifo);
    fifo_state_enum return_state = FIFO_SUCCESS;
    do
    {
        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_state;
}
