#include "ltrsrv_mspec_ltr35.h"
#include "ltrsrv_log.h"
#include <stdlib.h>
#include <stdio.h>

#define LTR35_FIFO_SIZE (8*1024*1024)

#define LTR35_CMD_STATUS  (LTR_CRATE_STREAM_CMD_INSTR|0x00)
#define LTR35_CMD_ECHO_CH (LTR_CRATE_STREAM_CMD_INSTR|0x0C)

typedef struct {
    uint8_t status_en; /* разрешение контроля заполненности буфера по периодическим статусам */
    uint8_t stream_mode; /* признак, что разрешено потоковый режим */
    uint8_t wrds_sample; /* количество слов, соответствующих одному семплу */
    uint8_t cur_wrds;    /* сколько слов от текущего семпла передано */
    uint16_t status_period; /* интервал следования статусов в семплах,
                               вылетевших из буфера модуля */
} t_ltr35_spec_data;

static int f_proc_snd(t_ltr_crate *crate, t_ltr_module *module, uint32_t wrd) {
    /* по сбросу считаем, что очередь свободна и сбрасываем флаги переполнений */
    if ((wrd & LTR_CRATE_STREAM_TYPE_Msk)==LTR_CRATE_STREAM_TYPE_DATA) {
        /* отсчитываем количество переданных слов */
        t_ltr35_spec_data *spec_data = (t_ltr35_spec_data *)module->spec_data;
        if (spec_data->stream_mode && spec_data->status_en) {
            if (++spec_data->cur_wrds==spec_data->wrds_sample) {
                module->stat.hard_send_fifo_full++;
                spec_data->cur_wrds = 0;
            }
        }
    } else if ((wrd & LTR_CRATE_STREAM_TYPE_Msk)==LTR_CRATE_STREAM_TYPE_CMD) {
        if ((wrd & LTR_CRATE_STREAM_CMD_CODE_Msk) == LTR35_CMD_ECHO_CH) {
            t_ltr35_spec_data *spec_data = (t_ltr35_spec_data *)module->spec_data;
            wrd >>= 16;
            spec_data->status_en = (wrd & 0x0080) == 0;
            if (!spec_data->status_en) {
                module->stat.hard_send_fifo_full = 0;
            }
            ltrsrv_log_rec(LTRSRV_LOGLVL_DETAIL, module->log_str, 0,
                           "module fifo control enable status %s",
                           spec_data->status_en ? "on" : "off");
        }
    }
    return 0;
}




static int f_proc_rcv_cmd(t_ltr_crate* crate, t_ltr_module* module, uint32_t wrd) {
    int proc = 0;
    if ((wrd & LTR_CRATE_STREAM_CMD_CODE_Msk) == LTR35_CMD_STATUS) {

        t_ltr35_spec_data *spec_data = (t_ltr35_spec_data *)module->spec_data;
        wrd >>= 16;

        if (wrd & 0x8000) {
            /* данное слово приходит после сброса буфера и содержит необходимые
             * настройки, которые нужно знать для корректного слежения за
             * состоянием очереди. сохраняем настройки и сбрасываем информацию
             * по заполненности */
            spec_data->status_period = (wrd & 0x03FF)+1;
            spec_data->wrds_sample   = wrd & 0x0400 ? 1 : 2;
            spec_data->cur_wrds      = 0;
            spec_data->stream_mode   =  wrd & 0x0800 ? 1 : 0;
            module->stat.hard_send_fifo_full    = 0;
            module->stat.hard_send_fifo_underrun= 0;
            module->stat.hard_send_fifo_overrun = 0;
            ltrsrv_log_rec(LTRSRV_LOGLVL_DETAIL, module->log_str, 0,
                           "fifo clear, new config: mode - %s, words per sample - %d, status period - %d",
                           spec_data->stream_mode ? "stream" : "ring",
                           spec_data->wrds_sample, spec_data->status_period);
        } else if (spec_data->stream_mode && spec_data->status_en) {
            /* признак, что между посылками статуса было голодание */
            if (wrd & 0x4000) {
                if (!module->stat.hard_send_fifo_underrun)
                    ltrsrv_log_rec(LTRSRV_LOGLVL_WARN, module->log_str, 0, "fifo first underrun");
                if (module->stat.hard_send_fifo_underrun!=(uint32_t)-1)
                    module->stat.hard_send_fifo_underrun++;
            }

            /* признак, что между посылками статуса було переполнение буфера */
            if (wrd & 0x2000) {
                if (!module->stat.hard_send_fifo_overrun)
                    ltrsrv_log_rec(LTRSRV_LOGLVL_WARN, module->log_str, 0, "fifo first overrun");
                if (module->stat.hard_send_fifo_overrun!=(uint32_t)-1)
                    module->stat.hard_send_fifo_overrun++;
            }

            /* если буфер был заполнен и освободился, то планируем попробовать
             * передать еще данные */
            if (module->stat.hard_send_fifo_full==module->stat.hard_send_fifo_size) {
                ltr_crate_wait_send_rdy(crate);
            }

            /* периодическое состояние приходит на каждые 1024 семплов.
              эти команды обрабатываем внутри сервера, не выдавая наружу */
            if (module->stat.hard_send_fifo_full < spec_data->status_period) {
                module->stat.hard_send_fifo_full = 0;
            } else {
                module->stat.hard_send_fifo_full-=spec_data->status_period;
            }
            module->stat.hard_send_fifo_internal = (wrd & 0x1FFF) << 10;
        } else {
            ltrsrv_log_rec(LTRSRV_LOGLVL_WARN, module->log_str, 0, "unexpected status word");
        }
        proc = 1;
    }
    return proc;
}


static void f_spec_free(t_ltr_crate* crate, t_ltr_module* module) {
    free(module->spec_data);
    module->spec_data = NULL;
}


int ltrsrv_ltr35_init(t_ltr_module *module, uint32_t init_flags) {
    int err = 0;
    module->flags = LTR_MODULE_FLAGS_USE_SND_FIFO_STATUS | LTR_MODULE_FLAGS_HIGH_BAUD;
    module->stat.hard_send_fifo_size = LTR35_FIFO_SIZE;
    module->stat.hard_send_fifo_full = 0;
    module->func.proc_snd = f_proc_snd;
    module->func.proc_rcv_cmd = f_proc_rcv_cmd;
    module->func.free = f_spec_free;

    module->spec_data = malloc(sizeof(t_ltr35_spec_data));
    if (module->spec_data==NULL) {
        err = LTRSRV_ERR_MEMALLOC;
    } else {
        memset(module->spec_data, 0, sizeof(t_ltr35_spec_data));
        if (init_flags & LTR_MODULE_INIT_FLAGS_ON_RESET) {
            sprintf(module->name, LTR_MODULE_NAME "35-%d", (init_flags & 3)+1);
        }
    }
    return err;
}
