#include "lip_ll_gd32f4xx_descr.h"
#include "lip/lip_log.h"
#include "lip/ll/lip_ll.h"
#include "lip/link/mac/tx/lip_mac_tx.h"
#include "lip/link/mac/rx/lip_mac_rx.h"
#include "gd32f4xx_enet.h"
#include <string.h>

#define __dmb() asm volatile ("dmb" ::: "memory")   /* data memory barrier, e.g. before DMA */


#define LIP_ETH_RX_NEXT_IND(ind) ((ind)==(LIP_PORT_RX_DESCR_CNT - 1) ? 0 : (ind) + 1)
#define LIP_ETH_TX_NEXT_IND(ind) ((ind)==(LIP_PORT_TX_DESCR_CNT - 1) ? 0 : (ind) + 1)


#define ETH_TX_FLAGS (TXDESCR_CTL_FS | TXDESCR_CTL_LS|ENET_CHECKSUM_TCPUDPICMP_FULL)


#define ETH_BUF_ALIGN  4

#define LSPEC_ALIGNMENT ETH_BUF_ALIGN

/* Массив дескрипторов DMA на прием */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(volatile t_eth_rx_descr  f_rx_descr[LIP_PORT_RX_DESCR_CNT]);

/* Массив дескрпиторов DMA на передачу */
#include "lcspec_align.h"
LIP_PORT_DESCR_MEM(volatile t_eth_tx_descr  f_tx_descr[LIP_PORT_TX_DESCR_CNT]);
static t_lip_tx_frame  *f_tx_frames[LIP_PORT_TX_DESCR_CNT];

/* Массив данных в который сохраняются входные пакеты */
#include "lcspec_align.h"
LIP_PORT_BUF_MEM(volatile uint8_t f_rx_bufs[LIP_PORT_RX_FULL_BUF_SIZE]);

/* номер дескриптора, соответствующий началу принимаемого пакета */
static int f_rx_cur_descr;

/* номер дескриптора, который будет использован для передачи след. пакета */
static int f_tx_put_descr;
/* номер дескриптора, следующий за последним, по которому завершена передача */
static int f_tx_done_descr;
/* кол-во дескрипторов, по которым начата, но не завершена передача */
static int f_tx_descr_progr_cnt;

static LINLINE void *f_rdes_buf_addr(int idx) {
    return  (void*)&f_rx_bufs[idx * LIP_PORT_RX_BUF_SIZE];
}

uint32_t lip_ll_gd32descr_rx_init(void) {
    unsigned i;
    memset((void*)&f_rx_descr[0], 0, sizeof(f_rx_descr));
    for (i=0; i < LIP_PORT_RX_DESCR_CNT; i++) {
        f_rx_descr[i].addr1 = (uint8_t*)f_rdes_buf_addr(i);

        if (i == (LIP_PORT_RX_DESCR_CNT - 1)) {
            f_rx_descr[i].ctl_size1 = RXDESCR_CTL_RER | LIP_PORT_RX_LAST_BUF_SIZE ;
        } else {
            f_rx_descr[i].ctl_size1 = LIP_PORT_RX_BUF_SIZE;
        }
        f_rx_descr[i].status = ENET_RDES0_DAV;
    }

    f_rx_cur_descr = 0;
    return  (uint32_t)&f_rx_descr[0];

}

uint32_t lip_ll_gd32descr_tx_init(void) {
    memset((void*)&f_tx_descr[0], 0, sizeof(f_tx_descr));
    /* сам массив дескрипторов реально инициализируется при начале передачи,
       поэтому здесь доп. инициализация не требуется */

    f_tx_put_descr = f_tx_done_descr = 0;
    f_tx_descr_progr_cnt = 0;

    return (uint32_t)&f_tx_descr[0];
}


enum payload_type{
    unsupported_payload = 0,
    payload_is_UDP,
    payload_is_TCP,
    payload_is_ICMP
};

/***************************************************************************
 *  Получение буфера с принятыми данными. Возвращает указатель на буфер и
 *  длину (через length). Если нет принятого пакета - возвращает 0
 ***************************************************************************/
#define REDS4_IP_frame_payload_error BIT(4)
#define REDS4_IP_frame_header_error BIT(3)
#define RDES0_Descriptor_error  BIT(14)
#define RDES0_Overflow_error BIT(11)
#define RDES0_Late_collision BIT(6)
#define RDES0_Watchdog_timeout BIT(4)
#define RDES0_Receive_error BIT(3)
#define RDES0_CRC_error  BIT(1)

#define RDES0_Errors (RDES0_Descriptor_error|RDES0_Overflow_error|RDES0_Late_collision|RDES0_Watchdog_timeout|RDES0_Receive_error|RDES0_CRC_error)

static void f_rx_check_descr(void) {
    t_lip_rx_ll_frame rx_frame;
    uint8_t* buf_start = 0;
    int cur_descr_ind = f_rx_cur_descr;
    /* если пришел новый дескриптор */
    while (!(f_rx_descr[cur_descr_ind].status & ENET_RDES0_DAV)) {
        volatile t_eth_rx_descr *cur_descr = &f_rx_descr[cur_descr_ind];
        int cur_err = 0;
        int cur_finish = 0;
        if ((cur_descr->status & RDES0_Errors) ||
                (!(cur_descr->status & ENET_RDES0_FDES) && (cur_descr_ind==f_rx_cur_descr))) {
            /* при ошибке выкидываем все пакеты до этой ошибки */
            lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                       "lip port: rx error. status = 0x%08x. drop descr from = %d to = %d\n",
                        cur_descr->status, f_rx_cur_descr, cur_descr_ind);
            cur_err = 1;
        } else {
            /* для первого дескриптора сохраняем адрес начала принятого кадра */
            if (cur_descr_ind == f_rx_cur_descr) {
                buf_start = f_rdes_buf_addr(cur_descr_ind);
            }
            if (cur_descr->status & RXDESCR_ST_LS) {
                /* последний дескриптор => принят весь кадр - возвращаем размер и адрес с начала */
                {
                    /* В дескрипторе сохраняется размер с CRC - вычитаем его */
                    //*length = RXDESCR_ST_GET_FRATE_LEN(cur_descr->status)-4;
                    size_t size;
                    size = GET_RDES0_FRML(cur_descr->status);
                    size = size - 4U;

                    /* if is a type frame, and CRC is not included in forwarding frame */
                    if((RESET != (ENET_MAC_CFG & ENET_MAC_CFG_TFCD)) && (RESET != (cur_descr->status & ENET_RDES0_FRMT))) {
                        size = size + 4U;
                    }
                    rx_frame.info.ll_flags = 0;
                    rx_frame.info.flags = 0;
                    rx_frame.pl_main.data = buf_start;
                    /* В дескрипторе сохраняется размер с CRC - вычитаем его */
                    rx_frame.pl_main.size = size;
                    // TODO: сделать размеры буферов одинаковые и проверять перехлест
                    //rx_frame.pl_tail.data = NULL;
                    //rx_frame.pl_tail.size = 0;
                    lip_mac_rx_proc_frame(&rx_frame);
                }


                cur_finish = 1;
            } else {
                /* последний дескриптор размером с макс. пакет (чтобы данные всегда были подряд),
                 * поэтому в нем всегда должен быть полный сегмент, если не так -
                 * то это ошибка ! */
                if (cur_descr_ind == (LIP_PORT_RX_DESCR_CNT - 1)) {
                    lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                               "lip port: rx last packet err. status = 0x%08x\n",
                                cur_descr->status);
                    cur_err = 1;
                }
            }
        }


        if (cur_err || cur_finish) {
            int i;
            buf_start=0;
            /* при ошибке выкидываем все части этого пакета,
             * и начинаем искать действительный пакет дальше */
            for (i=f_rx_cur_descr; i < (cur_descr_ind+1); i++) {
                f_rx_descr[i].status = ENET_RDES0_DAV;
            }
            f_rx_cur_descr = cur_descr_ind = LIP_ETH_RX_NEXT_IND(cur_descr_ind);
            __dmb();
            lip_ll_gd32descr_rx_poll_req();
        } else {
            cur_descr_ind = LIP_ETH_RX_NEXT_IND(cur_descr_ind);
        }
    }
}

/****************************************************************************
 * Возвращает указатель на буфер, в котором можно формировать следующий
 * пакет (0, если нет свободного места для пакета)
 * В max_length возвращается максимальный размер пакета
 *   (в данной реализации - всегда LIP_ETH_TX_BUF_SIZE)
 ****************************************************************************/
static void f_tx_check_descr(void) {
    /* сперва проверяем, какие дескрипторы завершились */
    while ((f_tx_descr_progr_cnt != 0) &&
            !(f_tx_descr[f_tx_done_descr].ctl_st & TXDESCR_CTLST_OWN)) {
        int tx_descr_num = f_tx_done_descr;
        volatile t_eth_tx_descr *tx_descr = &f_tx_descr[tx_descr_num];
        t_lip_tx_frame  *frame = f_tx_frames[tx_descr_num];

        if (tx_descr->ctl_st & TXDESCR_ST_ES) {
            frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_ERROR;
            lip_printf(LIP_LOG_MODULE_PORT, LIP_LOG_LVL_DEBUG_HIGH, 0,
                       "lip port: tx buf done with error! status = 0x%08X\n",
                       f_tx_descr[f_tx_done_descr].ctl_st);

            if (tx_descr->ctl_st  & TXDESCR_ST_UF) {
                /* сбрасываем статус ошибки */
               lip_ll_gd32descr_clear_stat();
               /* указываем DMA, что нужно проверить дескриптор */
               lip_ll_gd32descr_tx_poll_req();
            }
        }


        f_tx_done_descr = LIP_ETH_TX_NEXT_IND(f_tx_done_descr);
        f_tx_descr_progr_cnt--;
        frame->status.flags |= LIP_FRAME_TXSTATUS_FLAG_FINISH;
        lip_mac_tx_frame_done_cb(frame);
    }
}

t_lip_errs lip_ll_tx_frame(t_lip_tx_frame *frame) {
    uint32_t hdr_size = frame->buf.cur_ptr - frame->buf.start_ptr;
    const uint32_t data_size = frame->exdata_size;
    const uint8_t *exdata_ptr = frame->exdata_ptr;

    /* проверяем наличие свободного места */
    if (f_tx_descr_progr_cnt >= LIP_PORT_TX_DESCR_CNT) {
        return LIP_ERR_BUF_NOT_RDY;
    }

    f_tx_frames[f_tx_put_descr] = frame;

    f_tx_descr[f_tx_put_descr].addr1 = frame->buf.start_ptr;
    f_tx_descr[f_tx_put_descr].size1 = hdr_size;
    f_tx_descr[f_tx_put_descr].addr2 = exdata_ptr;
    f_tx_descr[f_tx_put_descr].size2 = data_size;

    asm volatile("": : :"memory");
    uint32_t ctl_st;
    /* конец цикла */
    ctl_st = ETH_TX_FLAGS | TXDESCR_CTLST_OWN | TXDESCR_CTL_IC |
	(f_tx_put_descr == (LIP_PORT_TX_DESCR_CNT - 1) ? TXDESCR_CTL_TER : 0);

    //lprintf("%x %x %x\n", f_tx_descr[f_tx_put_descr].addr1, f_tx_descr[f_tx_put_descr].size1, ctl_st);

    f_tx_descr[f_tx_put_descr].ctl_st = ctl_st;
    /* переходим к следующему дескриптору */
    f_tx_put_descr = LIP_ETH_TX_NEXT_IND(f_tx_put_descr);
    f_tx_descr_progr_cnt++;

    __dmb();
    /* указываем DMA, что нужно проверить дескриптор */
    lip_ll_gd32descr_tx_poll_req();

    return LIP_ERR_SUCCESS;
}



void lip_ll_pull(void) {
    const uint32_t ch_st = ENET_DMA_STAT;
    ENET_DMA_STAT = ch_st;
    if (ch_st & ENET_DMA_STAT_RS) {
        f_rx_check_descr();
    }
    if (ch_st & (ENET_DMA_STAT_TS)) {
        f_tx_check_descr();
    }
}

#ifdef LIP_CFG_USE_IRQ
void __attribute__((interrupt())) ENET_IRQHandler(void) {
    lip_port_pull();
}
#endif


void lip_ll_gd32descr_tx_poll_req(void) {
    /* resume DMA transmission by writing to the TPEN register*/
    ENET_DMA_TPEN = 0U;
}


void lip_ll_gd32descr_rx_poll_req(void) {
    ENET_DMA_RPEN = 0U;
}

void lip_ll_gd32descr_clear_stat(void) {
    ENET_DMA_STAT = (ENET_DMA_STAT_TU | ENET_DMA_STAT_TBU);
}



uint8_t *lip_ll_tx_get_buf(int *max_length) {
    return lip_ll_gd32descr_tx_get_buf(max_length);
}

t_lip_errs lip_ll_tx_flush_split(size_t size, const uint8_t* data, int data_size) {
    return lip_ll_gd32descr_tx_flush_split(size, data, data_size);
}


