#include "lip_phy_test_loop.h"
#if LIP_PHY_TEST_LOOPBACK_ENABLE
#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 "lip/link/mac/lip_mac.h"
#include "lip/phy/lip_phy.h"


#include "ltimer.h"

static int f_init_done = 0;
static uint32_t f_tx_cntr = 0;
static uint32_t f_rx_cntr = 0;
static t_lip_phy_test_loop_stats *f_stats = NULL;
/* настройки phy для режима loopback */
static const t_lip_phy_cfg f_loopback_phy_cfg = {
    .opmode = LIP_PHY_OPMODE_LOCAL_LOOP,
    .flags = LIP_PHY_CFG_FLAG_AN_DISABLED,
    .manual_mode = {
        .speed = LIP_PHY_SPEED_100,
        .flags = LIP_PHY_MODE_FLAG_DUPLEX_FULL,
    }
};
/* информация о передаваемых кадрах */
static t_lip_tx_frame_info f_tx_frame_info = {
    .flags = LIP_FRAME_TXINFO_FLAG_RAW  /* используем признак, что кадр не должен быть дополнительно обрабатываться */
};

static void f_rx_proc_data(const uint8_t *rx_buf, uint32_t rx_size) {
    uint32_t i;
    const uint32_t *rx_wrds = (const uint32_t *)rx_buf;


    for (i = 0; i < rx_size/4; ++i) {
        if (rx_wrds[i] != f_rx_cntr) {
            f_stats->last_err.exp_wrd = f_rx_cntr;
            f_stats->last_err.rcv_wrd = rx_wrds[i];
            f_stats->last_err.word_num = i;
            f_stats->last_err.packet_num = f_stats->rcv_packet_cnt;
            if (f_stats->rcv_err_cnt == 0) {
                f_stats->first_err = f_stats->last_err;
            }
            f_stats->rcv_err_cnt++;
            f_rx_cntr = rx_wrds[rx_size/4 - 1];
        }
        f_rx_cntr++;
    }
    f_stats->rcv_packet_cnt++;
    f_stats->rcv_cntr = f_rx_cntr;
}

static void f_rx_proc_frame(t_lip_rx_ll_frame *rx_frame) {
    if (f_stats) {
        f_rx_proc_data(rx_frame->pl_main.data, rx_frame->pl_main.size);
#if LIP_LL_PORT_FEATURE_RX_SPLIT
        if (rx_frame->pl_tail.data && (rx_frame->pl_tail.size > 0)) {
          f_rx_proc_data(rx_frame->pl_tail.data, rx_frame->pl_tail.size);
        }
#endif
    }
}




void lip_phy_test_loop_init(const t_lip_phy_test_loop_cfg *cfg, t_lip_phy_test_loop_stats *stats) {
    /* инициализация стека */
    f_init_done = 0;
    f_tx_cntr = f_rx_cntr = 0;
    f_stats = stats;
    memset(f_stats, 0, sizeof(t_lip_phy_test_loop_stats));

    /* инициализация MAC с запретом любой обработки входных пакетов и
     * фильтрации пакетов, чтобы можно было сравнить исходные данные с посылаемыми */
    lip_mac_init(LIP_INIT_FLAGS_MAC_RX_HWPROC_DIS | LIP_INIT_FLAGS_MAC_RX_FILTER_DIS | LIP_INIT_FLAGS_MAC_LOOPBACK);
    /* настройка phy на режим loopback */
    lip_mac_phy_set_cfg(&f_loopback_phy_cfg);

    lip_mac_rx_set_custom_proc_frame_cb(f_rx_proc_frame);
}

t_lip_errs lip_phy_test_loop_pull(void) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (f_stats) {
        err = lip_mac_pull();
        if (!err) {
            if (lip_mac_phy_ctx()->devinfo != NULL) {
                f_stats->flags |= LIP_PHY_TEST_LOOP_STAT_FLAG_PHY_DETECTED;
            } else {
                f_stats->flags &= ~LIP_PHY_TEST_LOOP_STAT_FLAG_PHY_DETECTED;
            }

            if (lip_mac_phy_ctx()->state.flags & LIP_PHY_STATE_FLAG_LINK_UP) {
                /* затем посылаем пакет, если есть место */
                t_lip_tx_frame *frame = lip_mac_tx_frame_prepare(&f_tx_frame_info);
                if (frame != NULL) {
                    uint32_t *tx_buf = (uint32_t*)frame->buf.start_ptr;
                    int i;
                    int max_size = (frame->buf.end_ptr - frame->buf.start_ptr) / 4;
                    for (i = 0; i < max_size; i++) {
                        *tx_buf++ = f_tx_cntr++;
                    }
                    frame->buf.cur_ptr = (uint8_t*)tx_buf;
                    /* передача сформированного пакета */
                    lip_mac_tx_frame_flush(frame);
                    f_stats->tx_packet_cnt++;
                }
            }
        }
    }
    return err;
}


void lip_phy_test_loop_finish(void) {    
    lip_mac_rx_set_custom_proc_frame_cb(NULL);
    lip_mac_close();
    f_stats = NULL;
}
#endif
