//******************************************** Включаемые файлы *********************************************

#include "streams.h"
#include "tcp.h"
#include "fpga_cmn.h"
#include "lmcal_sdio.h"
#include "lmcal_exmc.h"
#include "stream_ring_buffer.h"

//! Пока вычитываю данные в буфер МК из ПЛИС
//! Учесть, что можно добавить таймаут на обязательное чтение буфера
//! если поток медленный


// Номер буфер ФИФО ПЛИС соответствует номеру канала этого модуля

//******************************************* Локальные константы *******************************************

// Максимальное количество потоков на ввод
#define STREAM_IN_MAX_QTY       (16)

// Включение диагностики модуля
// Доступные значения: 1, 0
#define STREAMS_DEBUG           (1)


#define TASK_DEFAULT_VALUE      (0xFF)


//******************************************* Проверка параметров *******************************************

//******************************************** Определения типов ********************************************

typedef enum
{
    FSM_IN_READ_STS = 0U,
    FSM_IN_READ_SDIO,
    FSM_IN_WAIT_SDIO,
    // FSM_IN_SEND_TCP,

} fsm_stream_in_t;


//************************************ Глобальные (публичные) переменные ************************************

//************************************* Локальные (приватные) переменные ************************************

static bool b_init_done = false;
static bool b_enable = false;

static fsm_stream_in_t fsm_stream_in = FSM_IN_READ_STS;

// Статус FIFO на прием
static u32 fifo_in_full = 0;
static u32 fifo_in_half_full = 0;
static u32 fifo_in_empty = 0;


static bool task_sdio_read[STREAM_IN_CH_QTY];
static uint8_t cur_fifo_num = 0;


// Маска для доступа к флагам регистра статуса
static const u32 fifo_sts_mask[STREAM_IN_MAX_QTY] =
{
    FPGA_FIFO_0_EN_MASK,
    FPGA_FIFO_1_EN_MASK,
    FPGA_FIFO_2_EN_MASK,
    FPGA_FIFO_3_EN_MASK,
    FPGA_FIFO_4_EN_MASK,
    FPGA_FIFO_5_EN_MASK,
    FPGA_FIFO_6_EN_MASK,
    FPGA_FIFO_7_EN_MASK,
    FPGA_FIFO_8_EN_MASK,
    FPGA_FIFO_9_EN_MASK,
    FPGA_FIFO_10_EN_MASK,
    FPGA_FIFO_11_EN_MASK,
    FPGA_FIFO_12_EN_MASK,
    FPGA_FIFO_13_EN_MASK,
    FPGA_FIFO_14_EN_MASK,
    FPGA_FIFO_15_EN_MASK
};


// Адрес FIFO в ПЛИС
static const u32 fifo_addr[STREAM_IN_MAX_QTY] = 
{
   STREAM_IN_CH0_MMC_ADDR, 
   STREAM_IN_CH1_MMC_ADDR, 
   STREAM_IN_CH2_MMC_ADDR, 
   STREAM_IN_CH3_MMC_ADDR, 
   STREAM_IN_CH4_MMC_ADDR, 
   STREAM_IN_CH5_MMC_ADDR, 
   STREAM_IN_CH6_MMC_ADDR, 
   STREAM_IN_CH7_MMC_ADDR, 
   STREAM_IN_CH8_MMC_ADDR, 
   STREAM_IN_CH9_MMC_ADDR, 
   STREAM_IN_CH10_MMC_ADDR, 
   STREAM_IN_CH11_MMC_ADDR, 
   STREAM_IN_CH12_MMC_ADDR, 
   STREAM_IN_CH13_MMC_ADDR, 
   STREAM_IN_CH14_MMC_ADDR, 
   STREAM_IN_CH15_MMC_ADDR
};


// Кольцевые буфера на прием
static stream_rb_t fifo_in_rb[STREAM_IN_MAX_QTY];



//***********************************************************************************************************
//******************************** Определения локальных (приватных) функций ********************************
//***********************************************************************************************************



// перенести чтение регистров ПЛИС на верхний уровень (через колбек с возвратом -ОК\НОК)


// //-----------------------------------------------------------------------------------------------------------
// static bool f_read_sdio(void)
// {

// }


//-----------------------------------------------------------------------------------------------------------
static bool f_is_fifo_in_full(void)
{
    bool res = false;

    // Чтение регистра статуса FIFO (флаги)
    fpga_read_reg(FPGA_REG_STORAGE_STS2, &fifo_in_half_full);

    // Какие буферы читаем?
    // Формирование задания на чтение FIFO ПЛИС
    if (fifo_in_half_full != 0)
    {
        for (u8 i = 0; i < STREAM_IN_CH_QTY; i++)
        {
            if (fifo_in_half_full & fifo_sts_mask[i])
            {
                task_sdio_read[i] = true;

                //! Сохраняем последний номер ФИФО
                // cur_fifo_num = i;
            }
        }

        // cur_fifo_num = 0;

        res = true;
    }

    return res;
}




//-----------------------------------------------------------------------------------------------------------
static bool f_is_rd_task_is_empty(void)
{
    bool res = false;

    // Поиск задания в массиве
    cur_fifo_num = TASK_DEFAULT_VALUE;

    for (u8 i = 0; i < STREAM_IN_CH_QTY; i++)
    {
        if(task_sdio_read[i] == true)
        {
            task_sdio_read[i] = false;

            // Сохраняем номер буфера (канала\потока)
            cur_fifo_num = i;
            break;
        }
    }

    if (cur_fifo_num == TASK_DEFAULT_VALUE)
    {
        // Выполнили все задания
        res = true;
    }

    return res;
}


//-----------------------------------------------------------------------------------------------------------
// Чтение FIFO ПЛИС
//-----------------------------------------------------------------------------------------------------------
static void f_read_fifo(void)
{
    fun_res_t err = ERR_OK;
    u32 wrd_rdy;
    u32 wrd_rdy_cont;
    u32 wr_start;
    u32* wr_buf;
    
    // Сколько можем читать
    wrd_rdy = stream_rb_wr_rdy(&fifo_in_rb[cur_fifo_num]);

    if (wrd_rdy > 0)
    {
        // Сколько можем записать непрерывно
        wrd_rdy_cont = stream_rb_wr_rdy_cont(&fifo_in_rb[cur_fifo_num], NULL);

        // Ограничиваем макс. размером запроса
        if (wrd_rdy_cont >= STREAM_IN_SDIO_BLK_SIZE)
        {
            wrd_rdy_cont = STREAM_IN_SDIO_BLK_SIZE;
        }

        // адрес записи
        wr_start = stream_rb_get_wr_addr(&fifo_in_rb[cur_fifo_num]);
        wr_buf = &fifo_in_rb[cur_fifo_num].buf[wr_start];

        err = lmcal_sdio_read_buf(STREAM_IN_SDIO_CH, 
                                  fifo_addr[cur_fifo_num],
                                  wr_buf,
                                  wrd_rdy_cont,
                                  NULL);
        
        if (err == ERR_OK)
        {
            // Выполняем сдвиг адреса для следующей записи
            stream_rb_shift_wr_index(&fifo_in_rb[cur_fifo_num], wrd_rdy_cont);
        }
        else
        {
            // Произошла ошибка операции
        }


    }
    else
    {
        // Буфер в SDRAM переполнен!
        // todo - мигать красным светодмодом
    }

        
    // Чтение всех FIFO
    // Чтение выполняется асинхронно 
    // (нужно хранить текущее значение номера FIFO)

        // Учесть, что после чтения, нужно сдвинуть кольцевой буфер



        // lmcal_ch_sts_t sts;
        // sts = lmcal_sdio_get_status(STREAM_IN_SDIO_CH);

        // // Если SDIO не занят
        // if (sts != CH_STS_BUSY)
        // {
        //     // Обращение к актуальному каналу
        //     if ()
        //     {

        //     }


        //     err = lmcal_sdio_read_buf(LMCAL_SDIO_CHANNEL1, (u32)0x01000020, sdio_rx_data, 32, NULL);
        // }

}



//-----------------------------------------------------------------------------------------------------------
static void f_stream_in_loop(void)
{
    // todo - чтение по таймауту (медленный сбор)

    // Чтение статуса буферов
    if (fsm_stream_in == FSM_IN_READ_STS)
    {
        if (true == f_is_fifo_in_full())
        {
            fsm_stream_in = FSM_IN_READ_SDIO;
        }
    }

    // Проверка освобождения SDIO
    if (fsm_stream_in == FSM_IN_WAIT_SDIO)
    {
        err_or_res_t res;
        res = lmcal_sdio_get_status(STREAM_IN_SDIO_CH);
        
        if (res != CH_STS_BUSY)
        {
            fsm_stream_in = FSM_IN_READ_SDIO;

            // todo - смещение кольц.буфера


        }
    }
    
    // Выполнение заданий на чтение всех FIFO ПЛИС
    if (fsm_stream_in == FSM_IN_READ_SDIO)
    {
        if (true == f_is_rd_task_is_empty())
        {
            fsm_stream_in = FSM_IN_READ_STS;
        }
        else
        {
            f_read_fifo();
        }
    }


    
// вычитал из плис
// если размер буфера достаточен для формирования пакета tcp, то формируем его


}




// Запуск потока
static tcp_resp_errs_t f_cmd_stream_start(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
// todo 
    // // Пишем всю память субмодуля
    // err = (length != SU_MAX_MEM_SIZE) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    // TCP_CHECK_VAL(err);
    // err = (param > SU_SUBUNITS_MAX_QTY) ? (TCP_ERR_INVALID_CMD_PARAMS) : (TCP_ERR_OK);
    // TCP_CHECK_VAL(err);

    // subm_write((u8)param, SU_START_ADDR, (u8*)buf, (u8)length);

    TCP_EXIT;
    return err;
}



// Останов потока
static tcp_resp_errs_t f_cmd_stream_stop(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;

    // todo 
    TCP_EXIT;
    return err;
}



// Разрешение потока
static tcp_resp_errs_t f_cmd_stream_enable(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;

    //! Можем попросить присылать только некоторые каналы
// todo 
    TCP_EXIT;
    return err;
}



// Запрет потока
static tcp_resp_errs_t f_cmd_stream_disable(u32 param, const u8* buf, u32 length)
{
    tcp_resp_errs_t err = TCP_ERR_OK;
// todo 
    TCP_EXIT;
    return err;
}

//***********************************************************************************************************
//******************************* Определения глобальных (публичных) функций ********************************
//***********************************************************************************************************

//-----------------------------------------------------------------------------------------------------------
fun_res_t streams_init(void)
{
    fun_res_t err = ERR_OK;
    err_or_res_t res;

    err = L_ASSERT(true != b_init_done);
    L_CHECK_ERR(err);

    // Должна быть выполнена инициализация SDIO, EXMC
    res = lmcal_sdio_get_status(STREAM_IN_SDIO_CH);
    if ((res < ERR_OK) || (res == CH_STS_NOT_INIT))
    {
        err = ERR_ASSERT_ERROR;
        L_GOTO_EXIT;
    }

    // Команда запуска потока
    tcp_cmd_set_rx_callback(TCP_CMD_STREAMS_START, f_cmd_stream_start);
    tcp_cmd_set_rx_callback(TCP_CMD_STREAMS_STOP, f_cmd_stream_stop);
    tcp_cmd_set_rx_callback(TCP_CMD_STREAMS_ENABLE, f_cmd_stream_enable);
    tcp_cmd_set_rx_callback(TCP_CMD_STREAMS_DISABLE, f_cmd_stream_disable);

    // Инициализация кольцевых буферов
    #if (STREAM_IN_CH_QTY > 0)
    stream_rb_init(&fifo_in_rb[0],  STREAM_IN_CH0_RB_BUF,  STREAM_IN_CH0_RB_SIZE,  STREAM_IN_CH0_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 1)
    stream_rb_init(&fifo_in_rb[1],  STREAM_IN_CH1_RB_BUF,  STREAM_IN_CH1_RB_SIZE,  STREAM_IN_CH1_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 2)
    stream_rb_init(&fifo_in_rb[2],  STREAM_IN_CH2_RB_BUF,  STREAM_IN_CH2_RB_SIZE,  STREAM_IN_CH2_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 3)
    stream_rb_init(&fifo_in_rb[3],  STREAM_IN_CH3_RB_BUF,  STREAM_IN_CH3_RB_SIZE,  STREAM_IN_CH3_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 4)
    stream_rb_init(&fifo_in_rb[4],  STREAM_IN_CH4_RB_BUF,  STREAM_IN_CH4_RB_SIZE,  STREAM_IN_CH4_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 5)
    stream_rb_init(&fifo_in_rb[5],  STREAM_IN_CH5_RB_BUF,  STREAM_IN_CH5_RB_SIZE,  STREAM_IN_CH5_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 6)
    stream_rb_init(&fifo_in_rb[6],  STREAM_IN_CH6_RB_BUF,  STREAM_IN_CH6_RB_SIZE,  STREAM_IN_CH6_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 7)
    stream_rb_init(&fifo_in_rb[7],  STREAM_IN_CH7_RB_BUF,  STREAM_IN_CH7_RB_SIZE,  STREAM_IN_CH7_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 8)
    stream_rb_init(&fifo_in_rb[8],  STREAM_IN_CH8_RB_BUF,  STREAM_IN_CH8_RB_SIZE,  STREAM_IN_CH8_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 9)
    stream_rb_init(&fifo_in_rb[9],  STREAM_IN_CH9_RB_BUF,  STREAM_IN_CH9_RB_SIZE,  STREAM_IN_CH9_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 10)
    stream_rb_init(&fifo_in_rb[10], STREAM_IN_CH10_RB_BUF, STREAM_IN_CH10_RB_SIZE, STREAM_IN_CH10_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 11)
    stream_rb_init(&fifo_in_rb[11], STREAM_IN_CH11_RB_BUF, STREAM_IN_CH11_RB_SIZE, STREAM_IN_CH11_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 12)
    stream_rb_init(&fifo_in_rb[12], STREAM_IN_CH12_RB_BUF, STREAM_IN_CH12_RB_SIZE, STREAM_IN_CH12_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 13)
    stream_rb_init(&fifo_in_rb[13], STREAM_IN_CH13_RB_BUF, STREAM_IN_CH13_RB_SIZE, STREAM_IN_CH13_RB_MIN_FSIZE);
    #endif
    
    #if (STREAM_IN_CH_QTY > 14)
    stream_rb_init(&fifo_in_rb[14], STREAM_IN_CH14_RB_BUF, STREAM_IN_CH14_RB_SIZE, STREAM_IN_CH14_RB_MIN_FSIZE);
    #endif

    #if (STREAM_IN_CH_QTY > 15)
    stream_rb_init(&fifo_in_rb[15], STREAM_IN_CH15_RB_BUF, STREAM_IN_CH15_RB_SIZE, STREAM_IN_CH15_RB_MIN_FSIZE);
    #endif
    
    fsm_stream_in = FSM_IN_READ_STS;


    L_EXIT;
    return err;
}



//-----------------------------------------------------------------------------------------------------------
void streams_deinit(void)
{

}



//-----------------------------------------------------------------------------------------------------------
fun_res_t streams_enable(bool state)
{
    fun_res_t err = ERR_OK;


    return err;
}



//-----------------------------------------------------------------------------------------------------------
void streams_loop(void)
{
    // todo
    // if (true == b_enable)
    {
        // Поток на ввод
        f_stream_in_loop();

        //todo - Поток на вывод
    }
}


//*********************************************** КОНЕЦ ФАЙЛА ***********************************************

// Макрос, заполняющий структуру конфиграции

// #define STREAMS_IN_CH_CONFIG_FILL(channel)           \
// {                                                    \
//     .buf_size        = STREAM_CH##channel##_RB_SIZE, \
//     .min_free_size   = STREAM_CH##channel##_RB_MIN_FSIZE, \
//     .wr_start = 0, \
//     .wr_done  = 0, \
//     .rd_start = 0, \
//     .rd_done  = 0  \
// }

// .buf             = STREAM_CH##channel##_RB_BUF,



// static stream_rb_t fifo_in_rb[STREAM_IN_MAX_QTY] =
// {
//     STREAMS_IN_CH_CONFIG_FILL(0),
//     STREAMS_IN_CH_CONFIG_FILL(1),
//     STREAMS_IN_CH_CONFIG_FILL(2),
//     STREAMS_IN_CH_CONFIG_FILL(3),
//     STREAMS_IN_CH_CONFIG_FILL(4),
//     STREAMS_IN_CH_CONFIG_FILL(5),
//     STREAMS_IN_CH_CONFIG_FILL(6),
//     STREAMS_IN_CH_CONFIG_FILL(7),
//     STREAMS_IN_CH_CONFIG_FILL(8),
//     STREAMS_IN_CH_CONFIG_FILL(9),
//     STREAMS_IN_CH_CONFIG_FILL(10),
//     STREAMS_IN_CH_CONFIG_FILL(11),
//     STREAMS_IN_CH_CONFIG_FILL(12),
//     STREAMS_IN_CH_CONFIG_FILL(13),
//     STREAMS_IN_CH_CONFIG_FILL(14),
//     STREAMS_IN_CH_CONFIG_FILL(15),
// };
