/**
 * @file	   hal_gpio.c
 *
 * @copyright  Nehemis SARL reserves all rights even in the event of industrial
 *             property rights. We reserve all rights of disposal such as
 *             copying and passing on to third parties.
 *
 * @brief	   HAL layer for the GPIO module.
 *
 */

/******************************************************************************
 * Include Header Files
 ******************************************************************************/
#include "map_hal.h"
#include "hal_gpio.h"

/* CubeMX */
#include "gpio.h"

/******************************************************************************
 * Macro constant declarations
 ******************************************************************************/
#define HAL_GET_GPIO_OUTPUT_TYPE(port, pin) \
    ((((port)->OTYPER & (OUTPUT_TYPE << (pin))) == 0) ? GPIO_MODE_OUTPUT_PP : GPIO_MODE_OUTPUT_OD)


#define HAL_GPIO_GET_INTERRUPT_INPUT_MODE(port, pin) \
    (((((port)->MODER >> ((pin) * 2)) & 0x3) == GPIO_MODE_IT_RISING) || \
     ((((port)->MODER >> ((pin) * 2)) & 0x3) == GPIO_MODE_IT_FALLING) || \
     ((((port)->MODER >> ((pin) * 2)) & 0x3) == GPIO_MODE_IT_RISING_FALLING))

#define HAL_GPIO_MODE_MASK  (0x3u)

/******************************************************************************
 * Type declarations
 ******************************************************************************/


/******************************************************************************
 * Module Global Variable Declarations
 ******************************************************************************/
static uint32 (*halGpioCallbacks[MAP_HAL_GPIO_NUMBER])(HalGpioEdgeType_en);

/******************************************************************************
 * Static Function Declarations
 ******************************************************************************/

/******************************************************************************
 * Extern Function Definition
 ******************************************************************************/
uint32 HalGpioInit(void)
{
    return NMS_ERR_NONE;
}


uint32 HalGpioRead( MapHalGpioPin_en pin_en, HalGpioState_en * state_pen )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    uint16 targetPin_u16;
    GPIO_TypeDef* targetPort_pst = NULL;

    error_u32 = MapHalGpio(pin_en, &targetPin_u16, &targetPort_pst);

    if(error_u32 == NMS_ERR_NONE)
    {
        *state_pen = (HAL_GPIO_ReadPin(targetPort_pst, targetPin_u16) == GPIO_PIN_SET)
                                ? HAL_GPIO_STATE_HIGH
                                : HAL_GPIO_STATE_LOW;
    }
    else
    {
        *state_pen = HAL_GPIO_STATE_UNKNOWN;
        error_u32 = NMS_ERR_UNEXPECTED;
    }

    return error_u32;
}


uint32 HalGpioWrite( MapHalGpioPin_en pin_en, HalGpioState_en state_en )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    uint16 targetPin_u16;
    GPIO_TypeDef* targetPort_pst = NULL;

    error_u32 = MapHalGpio(pin_en, &targetPin_u16, &targetPort_pst);

    if(error_u32 == NMS_ERR_NONE)
    {
        uint32 outpuType_u32 = HAL_GET_GPIO_OUTPUT_TYPE(targetPort_pst, pin_en);

        if(outpuType_u32 == GPIO_MODE_OUTPUT_PP || outpuType_u32 == GPIO_MODE_OUTPUT_OD)
        {
            switch (state_en)
            {
            case HAL_GPIO_STATE_HIGH:
                HAL_GPIO_WritePin(targetPort_pst, targetPin_u16, GPIO_PIN_SET);		break;

            case HAL_GPIO_STATE_LOW:
                HAL_GPIO_WritePin(targetPort_pst, targetPin_u16, GPIO_PIN_RESET);	break;

            default:
                error_u32 = NMS_ERR_UNKNOWN;										break;
            }
        }

        HalGpioState_en  activeState_en = (HAL_GPIO_ReadPin(targetPort_pst, targetPin_u16) == GPIO_PIN_SET)
                                          ? HAL_GPIO_STATE_HIGH
                                          : HAL_GPIO_STATE_LOW;
        if(activeState_en != state_en)
        {
            error_u32 = NMS_ERR_INVALID_CONFIG;
        }

    }
    else
    {
        error_u32 = NMS_ERR_UNEXPECTED;
    }

    return error_u32;
}


uint32 HalGpioToggle( MapHalGpioPin_en pin_en )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    uint16 targetPin_u16;
    GPIO_TypeDef* targetPort_pst = NULL;

    error_u32 = MapHalGpio(pin_en, &targetPin_u16, &targetPort_pst);

    if(error_u32 == NMS_ERR_NONE)
    {
        uint32 outpuType_u32 = HAL_GET_GPIO_OUTPUT_TYPE(targetPort_pst, targetPin_u16);

        if(outpuType_u32 == GPIO_MODE_OUTPUT_PP || outpuType_u32 == GPIO_MODE_OUTPUT_OD)
        {
            HAL_GPIO_TogglePin(targetPort_pst, targetPin_u16);
        }
    }
    else
    {
        error_u32 = NMS_ERR_UNEXPECTED;
    }

    return error_u32;
}


uint32 HalGpioReConfigure( MapHalGpioPin_en pin_en, HalGpioType_en type_en )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    uint16 targetPin_u16;
    GPIO_TypeDef* targetPort_pst = NULL;
    GPIO_InitTypeDef gpioInit_st = {0};

    error_u32 = MapHalGpio(pin_en, &targetPin_u16, &targetPort_pst);

    if(error_u32 == NMS_ERR_NONE)
    {
        gpioInit_st.Pin   = targetPin_u16;

        switch(type_en)
        {
            case HAL_GPIO_INPUT:
                gpioInit_st.Mode             = GPIO_MODE_INPUT;
                gpioInit_st.Pull             = GPIO_NOPULL;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_OUTPUT_PUSH_PULL_UP:
                gpioInit_st.Mode             = GPIO_MODE_OUTPUT_PP;
                gpioInit_st.Pull             = GPIO_PULLUP;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_OUTPUT_PUSH_PULL_DOWN:
                gpioInit_st.Mode             = GPIO_MODE_OUTPUT_PP;
                gpioInit_st.Pull             = GPIO_PULLDOWN;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_OUTPUT_PUSH_PULL_FREE:
                gpioInit_st.Mode             = GPIO_MODE_OUTPUT_PP;
                gpioInit_st.Pull             = GPIO_NOPULL;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_OUTPUT_OPEN_DRAIN_UP:
                gpioInit_st.Mode             = GPIO_MODE_OUTPUT_OD;
                gpioInit_st.Pull             = GPIO_PULLUP;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_OUTPUT_OPEN_DRAIN_FREE:
                gpioInit_st.Mode             = GPIO_MODE_OUTPUT_OD;
                gpioInit_st.Pull             = GPIO_NOPULL;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_ISR_RISING:
                gpioInit_st.Mode             = GPIO_MODE_IT_RISING;
                gpioInit_st.Pull             = GPIO_NOPULL;
                error_u32                    = NMS_ERR_NONE;
                break;

            case HAL_GPIO_ISR_FALLING:
                gpioInit_st.Mode             = GPIO_MODE_IT_FALLING;
                gpioInit_st.Pull             = GPIO_NOPULL;
                error_u32                    = NMS_ERR_NONE;
                break;

            default: /* HAL_GPIO_ALTERNATE or unknown state. */
                error_u32 = NMS_ERR_INVALID_ARGUMENT;
                break;
        }
    }

    else
    {
        error_u32 = NMS_ERR_UNEXPECTED;
    }

    /* Deploy hardware modification. */
    if(error_u32 == NMS_ERR_NONE)
    {
        HAL_GPIO_DeInit(targetPort_pst, targetPin_u16);
        HAL_GPIO_Init(targetPort_pst, &gpioInit_st);
    }

    return error_u32;
}


uint32 HalGpioSetCallback( MapHalGpioPin_en pin_en, uint32 (*callback_pfn)(HalGpioEdgeType_en edge_en))
{
    uint32 error_u32 = NMS_ERR_NONE;

    /* Ideal values of edge are 0 or 1 for now */
    if ((pin_en < MAP_HAL_GPIO_NUMBER) && (callback_pfn != NULL))
    {
#ifdef DEBUG_ISSUE_EXTI_GPIO
        /* The check is always false. The configuration of interruption is not set in the GPIO settings, but in EXTI setttings.
         * This part of code need to be update to correct this feature (check the pin wanted is in interruption mode).
         * mbarth
         */
        GPIO_TypeDef* targetPort_pst = NULL;
        uint32 moder_u32 = HAL_GPIO_GET_INTERRUPT_INPUT_MODE(targetPort_pst, pin_en);

        if ((moder_u32 == GPIO_MODE_IT_RISING) || (moder_u32 == GPIO_MODE_IT_FALLING) || (moder_u32 == GPIO_MODE_IT_RISING_FALLING))
#endif
        {
            halGpioCallbacks[pin_en] = callback_pfn;
        }
    }
    else
    {
        error_u32 = NMS_ERR_UNEXPECTED;
    }

    return error_u32;
}

/******************************************************************************
 * Static Function Definitions
 ******************************************************************************/

/******************************************************************************
 * Callback Function Definitions
 ******************************************************************************/
/**
 * @brief      CubeMX GPIO  callback
 *
 * @param[in]  GPIO_Pin     The GPIO pin on which the interrupt occurs
 *
 */
void HAL_GPIO_EXTI_Rising_Callback( uint16 GPIO_Pin )
{
    uint32 error_u32 = NMS_ERR_NONE;
    MapHalGpioPin_en mappedPin_en;

    error_u32 = MapHalGpioCallback(GPIO_Pin, &mappedPin_en);

    if ((error_u32 == NMS_ERR_NONE) && (halGpioCallbacks[mappedPin_en] != NULL))
    {
        halGpioCallbacks[mappedPin_en](HAL_GPIO_EDGE_RISING);
    }
}


/**
 * @brief      CubeMX GPIO falling callback
 *
 * @param[in]  GPIO_Pin     The GPIO pin on which the interrupt occurs
 *
 */
void HAL_GPIO_EXTI_Falling_Callback( uint16 GPIO_Pin )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    MapHalGpioPin_en mappedPin_en;

    error_u32 = MapHalGpioCallback(GPIO_Pin, &mappedPin_en);

    if ((error_u32 == NMS_ERR_NONE) && (halGpioCallbacks[mappedPin_en] != NULL))
    {
        halGpioCallbacks[mappedPin_en](HAL_GPIO_EDGE_FALLING);
    }
}


/**
 * @brief      CubeMX GPIO callback
 *
 * @param[in]  GPIO_Pin     The GPIO pin on which the interrupt occurs
 *
 */
void HAL_GPIO_EXTI_Callback( uint16 GPIO_Pin )
{
    uint32 error_u32 = NMS_ERR_DEFAULT;
    MapHalGpioPin_en mappedPin_en;

    error_u32 = MapHalGpioCallback(GPIO_Pin, &mappedPin_en);

    if ((error_u32 == NMS_ERR_NONE) && (halGpioCallbacks[mappedPin_en] != NULL))
    {
        halGpioCallbacks[mappedPin_en](HAL_GPIO_EDGE_NONE);  /* Default value set for now*/
    }
}
