#include "lip_mac.h"
#include "lip/ll/lip_ll.h"
#include "lip_config.h"
#include "ltimer.h"
#include "lip/phy/lip_phy.h"
#include "lip/phy/lip_phy_cfg_defs.h"
#include "lip/link/lip_eth.h"
#include "lip/lip_log.h"
#include "tx/lip_mac_tx.h"
#include "rx/lip_mac_rx.h"
#if LIP_PHY_SUPPORT_KSZ8567
#include "lip/phy/ksz8567/lip_sw_ksz8567.h"
#endif

#ifndef LIP_PHY_RESET_WAIT_UP_TIME
    #define LIP_PHY_RESET_WAIT_UP_TIME 100
#endif


typedef enum  {
    LIP_MAC_STATE_NO_INIT,
    LIP_MAC_STATE_PIN_INIT_DONE,
    LIP_MAC_STATE_MAC_INIT_DONE,
    LIP_MAC_STATE_MAC_WORK,
    LIP_MAC_STATE_MAC_ERROR
} t_lip_mac_state;


static t_lip_mac_state f_mac_state;
static unsigned f_mac_cfg_flags;


#ifdef LIP_PHY_HARDWARE_RESET
    static t_ltimer f_hw_rst_timer;
    static bool f_rst_is_finish;

    static void f_phy_hwreset_start(t_lip_phy_ctx *phy_ctx) {
        /* аппаратный сброс phy */
        LIP_PHY_RESET_CFG();
        LIP_PHY_RESET_SET();
        f_rst_is_finish = false;
        ltimer_set_ms(&f_hw_rst_timer, LIP_PHY_RESET_TIME);
    }

    static bool f_phy_reset_check_done(t_lip_phy_ctx *phy_ctx) {
        bool done = ltimer_expired(&f_hw_rst_timer);
        if (done) {
            if (!f_rst_is_finish) {
                LIP_PHY_RESET_CLR();
                done = false;
                f_rst_is_finish = true;
                ltimer_set_ms(&f_hw_rst_timer, LIP_PHY_RESET_WAIT_UP_TIME);
            }
        }
        return done;
    }
#endif

static void f_mac_set_error(void) {
    f_mac_state = LIP_MAC_STATE_MAC_ERROR;
}


static t_lip_errs f_mac_init_finish(void) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (f_mac_state == LIP_MAC_STATE_PIN_INIT_DONE) {
        err = lip_ll_init(LIP_INIT_STAGE_MAC_INIT, f_mac_cfg_flags);
        if (err == LIP_ERR_SUCCESS) {
            lip_ll_set_mac_addr(lip_eth_cur_mac());
            f_mac_state = LIP_MAC_STATE_MAC_INIT_DONE;
        }
    }
    return err;
}

static t_lip_errs f_mac_try_start(void) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    if (f_mac_state == LIP_MAC_STATE_MAC_INIT_DONE) {
        const t_lip_phy_mode *mode = lip_mac_phy_mode();
        if (lip_phy_mode_is_valid(mode)) {
            lip_printf(LIP_LOG_MODULE_MAC, LIP_LOG_LVL_DETAIL, 0,
               "lip mac: update config - %d Mbit/s %s-duplex\n",
               lip_phy_speed_mbit(mode->speed),
               mode->flags & LIP_PHY_MODE_FLAG_DUPLEX_FULL ? "full" : "half");
            err = lip_ll_init(LIP_INIT_STAGE_MAC_START, f_mac_cfg_flags);
            if (err == LIP_ERR_SUCCESS) {
                f_mac_state = LIP_MAC_STATE_MAC_WORK;
            } else {
                f_mac_set_error();
            }
        }
    }
    return err;
}

#if !LIP_MAC_NO_PHY
static t_lip_phy_ctx f_mac_phy_ctx;

static t_lip_errs f_phy_predetect(t_lip_phy_ctx *phy_ctx);
static void f_mac_phy_mode_changed(t_lip_phy_ctx *phy_ctx);
static void f_mac_phy_link_status_changed(t_lip_phy_ctx *phy_ctx);

static const t_lip_phy_mdio_iface f_mac_phy_regs_iface = {
    .reg_wr_start = lip_ll_mdio_write_start,
    .reg_rd_start = lip_ll_mdio_read_start,
    .reg_get_rd_data = lip_ll_mdio_data,
    .iface_is_rdy = lip_ll_mdio_rdy,
} ;

static const t_lip_phy_iface_descr f_mac_phy_descr = {
    .name = "mac phy",
    .regs_iface = &f_mac_phy_regs_iface,
    .phy_addr_msk = LIP_PHY_DEFAULT_ADDR_MASK,
    .port_idx = 0,
    .flags = LIP_PHY_IFACE_FLAG_STATUS_POLL,
#ifdef LIP_PHY_HARDWARE_RESET
    .hw_reset.start =   f_phy_hwreset_start,
    .hw_reset.check_done = f_phy_reset_check_done,
#endif
    .cb.user_pre_detect     = f_phy_predetect,
    .cb.mode_changed        = f_mac_phy_mode_changed,
    .cb.link_status_changed = f_mac_phy_link_status_changed,
};

static t_lip_errs f_phy_predetect(t_lip_phy_ctx *phy_ctx) {
    return f_mac_init_finish();
}


static void f_mac_phy_mode_changed(t_lip_phy_ctx *phy_ctx) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    err = f_mac_init_finish();

    if (err == LIP_ERR_SUCCESS) {
        f_mac_try_start();
    }
}

static void f_mac_phy_link_status_changed(t_lip_phy_ctx *phy_ctx) {

}

void lip_mac_phy_set_cfg(const t_lip_phy_cfg *cfg) {
    lip_phy_set_cfg(&f_mac_phy_ctx, cfg);
    if (f_mac_state > LIP_MAC_STATE_PIN_INIT_DONE)
        f_mac_state = LIP_MAC_STATE_PIN_INIT_DONE;
}

const t_lip_phy_ctx *lip_mac_phy_ctx() {
    return &f_mac_phy_ctx;
}


#else
static t_lip_phy_mode f_mac_phy_mode;

void lip_mac_set_mode(const t_lip_phy_mode *mode) {
    f_mac_phy_mode = *mode;
}

#endif




t_lip_errs lip_mac_init(unsigned cfg_flags) {
    t_lip_errs  err = LIP_ERR_SUCCESS;

    f_mac_cfg_flags = cfg_flags;
    lip_max_tx_init();
    lip_max_rx_init();

#if !LIP_MAC_NO_PHY
    err = lip_phy_ctx_init(&f_mac_phy_ctx, &f_mac_phy_descr, NULL);
#endif
    if (err == LIP_ERR_SUCCESS) {
        err = lip_ll_init(LIP_INIT_STAGE_PHY_INTF_INIT, f_mac_cfg_flags);
    }
    if (err == LIP_ERR_SUCCESS) {
        f_mac_state = LIP_MAC_STATE_PIN_INIT_DONE;
    }
    return err;
}

#if !LIP_MAC_NO_PHY


#endif


const t_lip_phy_mode *lip_mac_phy_mode() {
#if !LIP_MAC_NO_PHY
    return &f_mac_phy_ctx.state.mode;
#else
    return &f_mac_phy_mode;
#endif
}


#if LIP_PHY_USE_STATS
const t_lip_phy_stats_results *lip_mac_phy_stats_results() {
    return lip_phy_stats_results(&f_mac_phy_ctx);
}
#endif




t_lip_errs lip_mac_pull(void) {
    t_lip_errs err = LIP_ERR_SUCCESS;
    lip_mac_rx_pull();
    lip_mac_tx_pull();
    lip_ll_pull();
#if !LIP_MAC_NO_PHY
    err = lip_phy_pull(&f_mac_phy_ctx);
#else
    if (f_mac_state != LIP_MAC_STATE_MAC_WORK) {
        if (f_mac_state == LIP_MAC_STATE_PIN_INIT_DONE) {
            err = f_mac_init_finish();
        }
        if (f_mac_state == LIP_MAC_STATE_MAC_INIT_DONE) {
            err = f_mac_try_start();
        }
    }
#endif
#if LIP_SW_SUPPORT_KSZ8567
    lip_sw_ksz8567_pull();
#endif
#if LIP_MAC_USER_PULL_CB
    lip_mac_user_pull();
#endif
    if (err != LIP_ERR_SUCCESS) {
        f_mac_set_error();
    }
    return err;
}



void lip_mac_close() {
    /* Выполняем сперва остановку обмена, затем отключение PHY, и затем уже
     * отключение mac, т.к. выключение PHY во время обмена может быть связана
     * с проблемами завершения обмена */
    lip_ll_stop();
    lip_max_tx_close();
    lip_max_rx_close();
#if !LIP_MAC_NO_PHY
    lip_phy_close(&f_mac_phy_ctx);
#endif
    f_mac_cfg_flags = 0;
    lip_ll_close();
}

bool lip_mac_is_initialized(void) {
    return (f_mac_state == LIP_MAC_STATE_MAC_WORK) || (f_mac_state == LIP_MAC_STATE_MAC_INIT_DONE);
}

bool lip_mac_in_work_state() {
    return (f_mac_state == LIP_MAC_STATE_MAC_WORK)
#if !LIP_MAC_NO_PHY
            && lip_phy_link_is_up(&f_mac_phy_ctx)
#else
            && lip_phy_mode_is_valid(lip_mac_phy_mode())
#endif
            ;
}


