/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : hal_stm32f4_eth.c
  * @brief          : STM32F4 specific Ethernet interface implementation
  ******************************************************************************
  */
/* USER CODE END Header */

#include <string.h>
#include "hal_stm32f4_eth.h"

/**
 * @brief Ethernet handles
 */
static hal_stm32f4_eth_handle_t eth_handles[HAL_ETH_INSTANCE_MAX];

/**
 * @brief Configure STM32F4 Ethernet GPIO
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_gpio_config(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* Enable Ethernet GPIO clocks */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOG_CLK_ENABLE();

    /* Configure ETH_MDIO (PA2) */
    GPIO_InitStruct.Pin = GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Configure ETH_RMII_REF_CLK (PA1) */
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Configure ETH_RMII_CRS_DV (PA7) */
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* Configure ETH_MDC (PC1) */
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* Configure ETH_RMII_RXD0 (PC4) */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* Configure ETH_RMII_RXD1 (PC5) */
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* Configure ETH_RMII_TX_EN (PB11) */
    GPIO_InitStruct.Pin = GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure ETH_RMII_TXD0 (PB12) */
    GPIO_InitStruct.Pin = GPIO_PIN_12;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure ETH_RMII_TXD1 (PB13) */
    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    return HAL_RET_OK;
}

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

    hal_eth_instance_t instance = config->instance;
    if (instance >= HAL_ETH_INSTANCE_MAX) {
        return HAL_RET_INVALID_PARAM;
    }

    /* Configure GPIO */
    if (hal_stm32f4_eth_gpio_config() != HAL_RET_OK) {
        return HAL_RET_INIT_ERROR;
    }

    /* Enable Ethernet clock */
    __HAL_RCC_ETH_CLK_ENABLE();

    /* Reset Ethernet handle */
    memset(&eth_handles[instance], 0, sizeof(hal_stm32f4_eth_handle_t));

    /* Configure Ethernet */
    eth_handles[instance].heth.Instance = ETH;
    eth_handles[instance].heth.Init.MACAddr = config->mac_addr.byte;
    eth_handles[instance].heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
    eth_handles[instance].heth.Init.TxDesc = 0x20000000;
    eth_handles[instance].heth.Init.RxDesc = 0x20000800;
    eth_handles[instance].heth.Init.RxBuffLen = 1524;
    eth_handles[instance].phy_addr = config->phy_addr;

    /* Initialize Ethernet */
    if (HAL_ETH_Init(&eth_handles[instance].heth) != HAL_OK) {
        return HAL_RET_INIT_ERROR;
    }

    /* Configure PHY */
    if (hal_stm32f4_eth_reset_phy(instance, config->phy_addr) != HAL_RET_OK) {
        return HAL_RET_INIT_ERROR;
    }

    /* Enable Ethernet interrupts if requested */
    if (config->interrupt_enable) {
        HAL_NVIC_SetPriority(ETH_IRQn, 5, 0);
        HAL_NVIC_EnableIRQ(ETH_IRQn);
    }

    eth_handles[instance].initialized = 1;
    return HAL_RET_OK;
}

/**
 * @brief Deinitialize STM32F4 Ethernet module
 * @param instance: Ethernet instance
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_deinit(hal_eth_instance_t instance)
{
    if (instance >= HAL_ETH_INSTANCE_MAX) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_OK;
    }

    /* Disable Ethernet interrupt */
    HAL_NVIC_DisableIRQ(ETH_IRQn);

    /* Deinitialize Ethernet */
    if (HAL_ETH_DeInit(&eth_handles[instance].heth) != HAL_OK) {
        return HAL_RET_DEINIT_ERROR;
    }

    /* Disable Ethernet clock */
    __HAL_RCC_ETH_CLK_DISABLE();

    eth_handles[instance].initialized = 0;
    return HAL_RET_OK;
}

/**
 * @brief Get STM32F4 Ethernet status
 * @param instance: Ethernet instance
 * @param status: Pointer to Ethernet status structure
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_get_status(hal_eth_instance_t instance, hal_eth_status_t* status)
{
    if (instance >= HAL_ETH_INSTANCE_MAX || !status) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Check link status */
    uint8_t link_up;
    if (hal_stm32f4_eth_check_link(instance, &link_up) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    status->link_up = link_up;
    status->rx_packets = eth_handles[instance].rx_packets;
    status->tx_packets = eth_handles[instance].tx_packets;
    status->rx_errors = eth_handles[instance].rx_errors;
    status->tx_errors = eth_handles[instance].tx_errors;

    /* Get speed and duplex mode */
    if (link_up) {
        uint16_t phy_status;
        if (hal_stm32f4_eth_read_phy_reg(instance, eth_handles[instance].phy_addr, 0x01, &phy_status) == HAL_RET_OK) {
            status->duplex = (phy_status & (1 << 13)) ? 1 : 0;
            status->speed = (phy_status & (1 << 12)) ? 100 : 10;
        } else {
            status->duplex = 0;
            status->speed = 10;
        }
    } else {
        status->duplex = 0;
        status->speed = 0;
    }

    return HAL_RET_OK;
}

/**
 * @brief Set STM32F4 Ethernet MAC address
 * @param instance: Ethernet instance
 * @param mac_addr: Pointer to MAC address structure
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_set_mac_addr(hal_eth_instance_t instance, const hal_eth_mac_addr_t* mac_addr)
{
    if (instance >= HAL_ETH_INSTANCE_MAX || !mac_addr) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* MAC address is set during initialization, cannot be changed at runtime */
    memcpy(eth_handles[instance].heth.Init.MACAddr, mac_addr->byte, 6);

    return HAL_RET_OK;
}

/**
 * @brief Get STM32F4 Ethernet MAC address
 * @param instance: Ethernet instance
 * @param mac_addr: Pointer to MAC address structure
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_get_mac_addr(hal_eth_instance_t instance, hal_eth_mac_addr_t* mac_addr)
{
    if (instance >= HAL_ETH_INSTANCE_MAX || !mac_addr) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Get MAC address from initialization structure */
    memcpy(mac_addr->byte, eth_handles[instance].heth.Init.MACAddr, 6);

    return HAL_RET_OK;
}

/**
 * @brief Check STM32F4 Ethernet link status
 * @param instance: Ethernet instance
 * @param link_up: Pointer to store link up status
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_check_link(hal_eth_instance_t instance, uint8_t* link_up)
{
    if (instance >= HAL_ETH_INSTANCE_MAX || !link_up) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    /* Read PHY status register */
    uint16_t phy_status;
    if (hal_stm32f4_eth_read_phy_reg(instance, eth_handles[instance].phy_addr, 0x01, &phy_status) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Check link status bit */
    *link_up = (phy_status & (1 << 2)) ? 1 : 0;

    return HAL_RET_OK;
}

/**
 * @brief Reset STM32F4 Ethernet PHY
 * @param instance: Ethernet instance
 * @param phy_addr: PHY address
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_reset_phy(hal_eth_instance_t instance, hal_eth_phy_addr_t phy_addr)
{
    if (instance >= HAL_ETH_INSTANCE_MAX) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    eth_handles[instance].phy_addr = phy_addr;

    /* Reset PHY */
    uint16_t value = 0x8000;
    if (hal_stm32f4_eth_write_phy_reg(instance, phy_addr, 0x00, value) != HAL_RET_OK) {
        return HAL_RET_ERROR;
    }

    /* Wait for reset to complete */
    uint32_t timeout = 1000;
    while (timeout--) {
        if (hal_stm32f4_eth_read_phy_reg(instance, phy_addr, 0x00, &value) == HAL_RET_OK) {
            if (!(value & 0x8000)) {
                break;
            }
        }
        HAL_Delay(1);
    }

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

    return HAL_RET_OK;
}

/**
 * @brief Read STM32F4 Ethernet PHY register
 * @param instance: Ethernet instance
 * @param phy_addr: PHY address
 * @param reg_addr: Register address
 * @param value: Pointer to store register value
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_read_phy_reg(hal_eth_instance_t instance, hal_eth_phy_addr_t phy_addr, uint16_t reg_addr, uint16_t* value)
{
    if (instance >= HAL_ETH_INSTANCE_MAX || !value) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    uint32_t reg_value;
    if (HAL_ETH_ReadPHYRegister(&eth_handles[instance].heth, phy_addr, reg_addr, &reg_value) != HAL_OK) {
        return HAL_RET_ERROR;
    }
    *value = (uint16_t)reg_value;

    return HAL_RET_OK;
}

/**
 * @brief Write STM32F4 Ethernet PHY register
 * @param instance: Ethernet instance
 * @param phy_addr: PHY address
 * @param reg_addr: Register address
 * @param value: Register value
 * @retval HAL status code
 */
hal_ret_t hal_stm32f4_eth_write_phy_reg(hal_eth_instance_t instance, hal_eth_phy_addr_t phy_addr, uint16_t reg_addr, uint16_t value)
{
    if (instance >= HAL_ETH_INSTANCE_MAX) {
        return HAL_RET_INVALID_PARAM;
    }

    if (!eth_handles[instance].initialized) {
        return HAL_RET_INIT_ERROR;
    }

    if (HAL_ETH_WritePHYRegister(&eth_handles[instance].heth, phy_addr, reg_addr, value) != HAL_OK) {
        return HAL_RET_ERROR;
    }

    return HAL_RET_OK;
}

/**
 * @brief Get STM32F4 Ethernet handle
 * @param instance: Ethernet instance
 * @retval Pointer to Ethernet handle structure
 */
hal_stm32f4_eth_handle_t* hal_stm32f4_eth_get_handle(hal_eth_instance_t instance)
{
    if (instance >= HAL_ETH_INSTANCE_MAX) {
        return NULL;
    }

    if (!eth_handles[instance].initialized) {
        return NULL;
    }

    return &eth_handles[instance];
}

/**
 * @brief Ethernet interrupt handler
 */
void ETH_IRQHandler(void)
{
    HAL_ETH_IRQHandler(&eth_handles[0].heth);
}

/**
 * @brief Ethernet RX callback
 * @param heth: Ethernet handle
 */
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
    /* Increment received packets count */
    eth_handles[0].rx_packets++;

    /* Reactivate reception */
    HAL_ETH_Start_IT(heth);
}

/**
 * @brief Ethernet TX callback
 * @param heth: Ethernet handle
 */
void HAL_ETH_TxCpltCallback(ETH_HandleTypeDef *heth)
{
    /* Increment transmitted packets count */
    eth_handles[0].tx_packets++;
}

/**
 * @brief Ethernet error callback
 * @param heth: Ethernet handle
 */
void HAL_ETH_ErrorCallback(ETH_HandleTypeDef *heth)
{
    /* Increment error count */
    eth_handles[0].rx_errors++;
    eth_handles[0].tx_errors++;
}
