#include "system.h"

#include "platform.h"

#include "term.h"
#include "led.h"
#include "rf_rx.h"

void system_init()
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    HAL_Init();

    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_PWR_CLK_ENABLE();

    // Configure the main internal regulator output voltage
    HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

    // Initializes the RCC Oscillators according to the specified parameters in the RCC_OscInitTypeDef structure.
    RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState            = RCC_HSI_ON;
    RCC_OscInitStruct.HSIDiv              = RCC_HSI_DIV1;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource       = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM            = RCC_PLLM_DIV1;
    RCC_OscInitStruct.PLL.PLLN            = 8;
    RCC_OscInitStruct.PLL.PLLP            = RCC_PLLP_DIV2;
    RCC_OscInitStruct.PLL.PLLR            = RCC_PLLR_DIV2;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) system_die();

    // Initializes the CPU, AHB and APB buses clocks
    RCC_ClkInitStruct.ClockType       = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1;
    RCC_ClkInitStruct.SYSCLKSource    = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider   = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider  = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) system_die();

    // Check standby
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET){
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
        if (__HAL_PWR_GET_FLAG(BTN_WAKEUP_FLAG) != RESET){
            __HAL_PWR_CLEAR_FLAG(BTN_WAKEUP_FLAG);

        }
    }

}

void system_shutdown()
{
    // RF off
    RF_RX_TIM_STOP();
    RF_RX_POWER_OFF();
    // Show off
    led_set_color(LED_COLOR_RED);
    HAL_Delay(100);
    led_set_color(LED_COLOR_OFF);
    HAL_Delay(100);
    led_set_color(LED_COLOR_RED);
    HAL_Delay(100);
    led_set_color(LED_COLOR_OFF);
    LED_POWER_OFF();
    // Message
    term_out_str("Entering standby...\r\n");
    term_out_flush();
    // Reset GPIO
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Pin = GPIO_PIN_All;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    GPIO_InitStruct.Pin = GPIO_PIN_All & ~(GPIO_PIN_0);
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    __HAL_RCC_GPIOA_CLK_DISABLE();
    __HAL_RCC_GPIOB_CLK_DISABLE();
    __HAL_RCC_GPIOC_CLK_DISABLE();
    // Wait
    HAL_Delay(100);
    // Standby
    HAL_PWR_EnableWakeUpPin(BTN_WAKEUP_PIN);
    __HAL_PWR_CLEAR_FLAG(BTN_WAKEUP_FLAG);
    HAL_PWR_EnterSTANDBYMode();
}

void system_reboot()
{
    term_out_str("Reboot...\r\n");
    term_out_flush();
    HAL_Delay(100);
    NVIC_SystemReset();
}

void system_die_ex(char *file, int line)
{
    while(1){
        __NOP();
    }
}

void system_gpio_clk_enable(GPIO_TypeDef *port)
{
    if (port==GPIOA){
        if (__HAL_RCC_GPIOA_IS_CLK_DISABLED()) __HAL_RCC_GPIOA_CLK_ENABLE();
    }else if (port==GPIOB){
        if (__HAL_RCC_GPIOB_IS_CLK_DISABLED()) __HAL_RCC_GPIOB_CLK_ENABLE();
    }else if (port==GPIOC){
        if (__HAL_RCC_GPIOC_IS_CLK_DISABLED()) __HAL_RCC_GPIOC_CLK_ENABLE();
    }else if (port==GPIOF){
        if (__HAL_RCC_GPIOF_IS_CLK_DISABLED()) __HAL_RCC_GPIOF_CLK_ENABLE();
    }else{
        system_die();
    }
}

// ============================================================================
//                             INTERRUPT HANDLER
// ============================================================================

void NMI_Handler(void)
{

}

void HardFault_Handler(void)
{
    system_die();
}

void SVC_Handler(void)
{

}

void PendSV_Handler(void)
{

}

void SysTick_Handler(void)
{
  HAL_IncTick();
}

