/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : bsp_eth.c
  * @brief          : Board support package Ethernet (LAN8720) driver implementation
  ******************************************************************************
  */
/* USER CODE END Header */

#include "bsp_eth.h"

/**
 * @brief Ethernet configuration
 */
static bsp_eth_config_t eth_config;
static uint8_t eth_initialized = 0;

/**
 * @brief Initialize Ethernet (LAN8720) module
 * @param config: Pointer to Ethernet configuration structure
 * @retval HAL status code
 */
hal_ret_t bsp_eth_init(const bsp_eth_config_t* config)
{
    if (!config) {
        return HAL_RET_INVALID_PARAM;
    }

    if (eth_initialized) {
        return HAL_RET_OK;
    }

    /* Store configuration */
    eth_config = *config;

    /* Convert to HAL configuration */
    hal_eth_config_t hal_config;
    hal_config.enable = config->enable;
    hal_config.instance = config->instance;
    hal_config.mode = config->mode;
    hal_config.speed = config->speed;
    hal_config.phy_addr = config->phy_addr;
    hal_config.mac_addr = config->mac_addr;
    hal_config.auto_negotiation = config->auto_negotiation;
    hal_config.interrupt_enable = config->interrupt_enable;

    /* Initialize HAL Ethernet */
    if (hal_eth_init(&hal_config) != HAL_RET_OK) {
        return HAL_RET_INIT_ERROR;
    }

    /* Detect LAN8720 PHY */
    uint8_t detected;
    if (bsp_eth_detect_phy(&detected) != HAL_RET_OK) {
        return HAL_RET_INIT_ERROR;
    }

    if (!detected) {
        return HAL_RET_INIT_ERROR;
    }

    /* Configure PHY */
    if (bsp_eth_configure_phy(config->mode, config->speed, config->auto_negotiation) != HAL_RET_OK) {
        return HAL_RET_INIT_ERROR;
    }

    eth_initialized = 1;
    return HAL_RET_OK;
}

/**
 * @brief Deinitialize Ethernet (LAN8720) module
 * @retval HAL status code
 */
hal_ret_t bsp_eth_deinit(void)
{
    if (!eth_initialized) {
        return HAL_RET_OK;
    }

    if (hal_eth_deinit(eth_config.instance) != HAL_RET_OK) {
        return HAL_RET_DEINIT_ERROR;
    }

    eth_initialized = 0;
    return HAL_RET_OK;
}

/**
 * @brief Get Ethernet (LAN8720) status
 * @param status: Pointer to Ethernet status structure
 * @retval HAL status code
 */
hal_ret_t bsp_eth_get_status(bsp_eth_status_t* status)
{
    if (!status) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Get HAL Ethernet status */
    hal_eth_status_t hal_status;
    if (hal_eth_get_status(eth_config.instance, &hal_status) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Copy status */
    status->link_up = hal_status.link_up;
    status->duplex = hal_status.duplex;
    status->speed = hal_status.speed;
    status->rx_packets = hal_status.rx_packets;
    status->tx_packets = hal_status.tx_packets;
    status->rx_errors = hal_status.rx_errors;
    status->tx_errors = hal_status.tx_errors;

    /* Get PHY identifiers */
    if (bsp_eth_read_phy_reg(LAN8720_REG_PID1, &status->phy_id1) != HAL_RET_OK) {
        status->phy_id1 = 0;
    }

    if (bsp_eth_read_phy_reg(LAN8720_REG_PID2, &status->phy_id2) != HAL_RET_OK) {
        status->phy_id2 = 0;
    }

    return HAL_RET_OK;
}

/**
 * @brief Set Ethernet MAC address
 * @param mac_addr: Pointer to MAC address structure
 * @retval HAL status code
 */
hal_ret_t bsp_eth_set_mac_addr(const hal_eth_mac_addr_t* mac_addr)
{
    if (!mac_addr) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_set_mac_addr(eth_config.instance, mac_addr) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    eth_config.mac_addr = *mac_addr;
    return HAL_RET_OK;
}

/**
 * @brief Get Ethernet MAC address
 * @param mac_addr: Pointer to MAC address structure
 * @retval HAL status code
 */
hal_ret_t bsp_eth_get_mac_addr(hal_eth_mac_addr_t* mac_addr)
{
    if (!mac_addr) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_get_mac_addr(eth_config.instance, mac_addr) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Check Ethernet link status
 * @param link_up: Pointer to store link up status
 * @retval HAL status code
 */
hal_ret_t bsp_eth_check_link(uint8_t* link_up)
{
    if (!link_up) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_check_link(eth_config.instance, link_up) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Reset Ethernet PHY (LAN8720)
 * @retval HAL status code
 */
hal_ret_t bsp_eth_reset_phy(void)
{
    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_reset_phy(eth_config.instance, eth_config.phy_addr) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Read LAN8720 PHY register
 * @param reg_addr: Register address
 * @param value: Pointer to store register value
 * @retval HAL status code
 */
hal_ret_t bsp_eth_read_phy_reg(uint16_t reg_addr, uint16_t* value)
{
    if (!value) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_read_phy_reg(eth_config.instance, eth_config.phy_addr, reg_addr, value) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Write LAN8720 PHY register
 * @param reg_addr: Register address
 * @param value: Register value
 * @retval HAL status code
 */
hal_ret_t bsp_eth_write_phy_reg(uint16_t reg_addr, uint16_t value)
{
    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (hal_eth_write_phy_reg(eth_config.instance, eth_config.phy_addr, reg_addr, value) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Detect LAN8720 PHY presence
 * @param detected: Pointer to store detection result
 * @retval HAL status code
 */
hal_ret_t bsp_eth_detect_phy(uint8_t* detected)
{
    if (!detected) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Read PHY identifiers */
    uint16_t id1, id2;
    if (bsp_eth_read_phy_reg(LAN8720_REG_PID1, &id1) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    if (bsp_eth_read_phy_reg(LAN8720_REG_PID2, &id2) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Check if this is a LAN8720 PHY */
    if (id1 == LAN8720_PID1_VALUE && (id2 & 0xFFF0) == LAN8720_PID2_VALUE) {
        *detected = 1;
    } else {
        *detected = 0;
    }

    return HAL_RET_OK;
}

/**
 * @brief Configure LAN8720 PHY
 * @param mode: Ethernet mode
 * @param speed: Ethernet speed
 * @param auto_neg: Auto-negotiation enable
 * @retval HAL status code
 */
hal_ret_t bsp_eth_configure_phy(hal_eth_mode_t mode, hal_eth_speed_t speed, uint8_t auto_neg)
{
    if (!eth_initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Read current BCR register */
    uint16_t bcr;
    if (bsp_eth_read_phy_reg(LAN8720_REG_BCR, &bcr) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Clear relevant bits */
    bcr &= ~(LAN8720_BCR_AUTO_NEG_EN | LAN8720_BCR_SPEED_SELECT | LAN8720_BCR_DUPLEX_MODE);

    if (auto_neg) {
        /* Enable auto-negotiation */
        bcr |= LAN8720_BCR_AUTO_NEG_EN;
        bcr |= LAN8720_BCR_RESTART_AN;
    } else {
        /* Manual configuration */
        if (speed == HAL_ETH_SPEED_100MBPS) {
            bcr |= LAN8720_BCR_SPEED_SELECT;
        }

        if (mode == HAL_ETH_MODE_FULLDUPLEX) {
            bcr |= LAN8720_BCR_DUPLEX_MODE;
        }
    }

    /* Write BCR register */
    if (bsp_eth_write_phy_reg(LAN8720_REG_BCR, bcr) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Wait for configuration to complete */
    if (auto_neg) {
        uint32_t timeout = 1000;
        uint16_t bsr;
        while (timeout--) {
            if (bsp_eth_read_phy_reg(LAN8720_REG_BSR, &bsr) == HAL_RET_OK) {
                if (bsr & LAN8720_BSR_AUTO_NEG_COMPLETE) {
                    break;
                }
            }
            HAL_Delay(1);
        }

        if (timeout == 0) {
            return HAL_RET_TIMEOUT;
        }
    }

    return HAL_RET_OK;
}
