#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stm32f1xx_hal.h>

// Set STM32F103 LED RED (PC13)
#define LED_PORT                GPIOB
#define LED_PIN                 GPIO_PIN_0
#define LED_PORT_CLK_ENABLE     __HAL_RCC_GPIOC_CLK_ENABLE

UART_HandleTypeDef huart1;
UART_HandleTypeDef huart2;
ADC_HandleTypeDef adcHandle;

uint16_t adcValue;

/* Private function prototypes */
void SystemClock_Config(void);
void RCC_SystemClock_Config(void);
void GPIO_Init(void);
void GPIO_Output_Config(void);
void ADC_Config(void);
uint16_t ADC_Read(void);
void Error_Handler(const char *error);
uint32_t getCurrentMicros(void);

static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);

uint32_t timeOfRun = 0;
uint8_t count = 0;

void SysTick_Handler(void)
{
    HAL_IncTick();

    // 1 Hz blinking
    if ((HAL_GetTick() % 500) == 0) {
        HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
    }

    if (HAL_GetTick() - timeOfRun >= 1000) {
      timeOfRun = HAL_GetTick();
      count = 0;
      printf("%ld ms\n", timeOfRun);
    }
}

int main(void)
{
    const char * hello_world = "Hello STM32\r\n";

    /* HAL initialization */
    HAL_Init();

    /* Set the SYSCLK at maximum frequency (72 MHz) */
    RCC_SystemClock_Config();

    GPIO_Init();
    GPIO_Output_Config();
    ADC_Config();

    MX_USART1_UART_Init();
    // MX_USART2_UART_Init();

    HAL_UART_Transmit(&huart1, (uint8_t *)hello_world, strlen(hello_world), HAL_MAX_DELAY);

    printf("Characters: %c %c\n", 'a', 65);
    printf("Decimals: %d %ld\n", 1977, 650000L);
    printf("Preceding with blanks: %10d\n", 1977);
    printf("Preceding with zeros: %010d\n", 1977);
    printf("Some different radices: %d %x %o %#x %#o\n", 100, 100, 100, 100, 100);
    printf("floats: %4.2f %+.0e %E\n", 3.1416, 3.1416, 3.1416);
    printf("Width trick: %*d\n", 5, 10);
    printf("%s\n", "A string");

    while (true)
    {
        adcValue = ADC_Read();

        if (adcValue > 2047)
        {
            /* Turn on yellow LED */
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET);
            /* Turn off red LED */
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
        }
        else
        {
            /* Turn off yellow LED */
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET);
            /* Turn on red LED */
            HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
        }

        printf("ADC (%d): %d\n", ++count, adcValue);
        HAL_Delay(250);
    }

    return 0;
}

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

    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler("SystemClock config error");
    }

    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) {
        Error_Handler("SystemClock config error");
    }
}

void RCC_SystemClock_Config(void)
{
    RCC_ClkInitTypeDef rccClkInit;
    RCC_OscInitTypeDef rccOscInit;

    rccOscInit.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    rccOscInit.HSEState       = RCC_HSE_ON;
    rccOscInit.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    rccOscInit.PLL.PLLState   = RCC_PLL_ON;
    rccOscInit.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    rccOscInit.PLL.PLLMUL     = RCC_PLL_MUL9;

    if (HAL_RCC_OscConfig(&rccOscInit) != HAL_OK)
    {
        Error_Handler("RCC config error");
    }

    rccClkInit.ClockType      = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
                                 RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
    rccClkInit.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    rccClkInit.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    rccClkInit.APB2CLKDivider = RCC_HCLK_DIV1;
    rccClkInit.APB1CLKDivider = RCC_HCLK_DIV2;

    if (HAL_RCC_ClockConfig(&rccClkInit, FLASH_LATENCY_2) != HAL_OK)
    {
        Error_Handler("RCC config error");
    }
}

void GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_Config;

    GPIO_Config.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Config.Pull = GPIO_NOPULL;
    GPIO_Config.Speed = GPIO_SPEED_FREQ_HIGH;
    GPIO_Config.Pin = LED_PIN;

    LED_PORT_CLK_ENABLE();
    HAL_GPIO_Init(LED_PORT, &GPIO_Config);

    /* Turn on PC13 */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
}

void GPIO_Output_Config(void)
{
    GPIO_InitTypeDef gpioInit;

    __HAL_RCC_GPIOB_CLK_ENABLE();

    gpioInit.Pin   = GPIO_PIN_7 | GPIO_PIN_8;
    gpioInit.Mode  = GPIO_MODE_OUTPUT_PP;
    gpioInit.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOB, &gpioInit);
}

void ADC_Config(void)
{
    ADC_ChannelConfTypeDef adcChannelConf;

    adcHandle.Instance                = ADC1;
    adcHandle.Init.DataAlign          = ADC_DATAALIGN_RIGHT;
    adcHandle.Init.ScanConvMode       = ADC_SCAN_DISABLE;
    adcHandle.Init.ContinuousConvMode = DISABLE;
    adcHandle.Init.ExternalTrigConv   = ADC_SOFTWARE_START;

    if (HAL_ADC_Init(&adcHandle) != HAL_OK)
    {
        Error_Handler("ADC config error");
    }

    adcChannelConf.Channel      = ADC_CHANNEL_0;
    adcChannelConf.Rank         = ADC_REGULAR_RANK_1;
    adcChannelConf.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;

    if (HAL_ADC_ConfigChannel(&adcHandle, &adcChannelConf) != HAL_OK)
    {
        Error_Handler("ADC config error");
    }

    if (HAL_ADCEx_Calibration_Start(&adcHandle) != HAL_OK)
    {
        Error_Handler("ADC calibration error");
    }
}

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)
{
    RCC_PeriphCLKInitTypeDef rccPeriphCLKInit;
    GPIO_InitTypeDef gpioInit;

    __HAL_RCC_ADC1_CLK_ENABLE();
    rccPeriphCLKInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    rccPeriphCLKInit.AdcClockSelection    = RCC_ADCPCLK2_DIV6;
    HAL_RCCEx_PeriphCLKConfig(&rccPeriphCLKInit);
    __HAL_RCC_GPIOA_CLK_ENABLE();

    gpioInit.Pin  = GPIO_PIN_0;
    gpioInit.Mode = GPIO_MODE_ANALOG;
    gpioInit.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &gpioInit);
}

uint16_t ADC_Read(void)
{
    uint16_t adcVal;

    HAL_ADC_Start(&adcHandle);
    if (HAL_ADC_PollForConversion(&adcHandle, HAL_MAX_DELAY) == HAL_OK)
    {
        adcVal = HAL_ADC_GetValue(&adcHandle);
    }
    HAL_ADC_Stop(&adcHandle);

    return adcVal;
}

void MX_USART1_UART_Init(void)
{
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    huart1.Instance = USART1;
    huart1.Init.BaudRate = 115200;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        Error_Handler("Falha na Inicialização da UART 1");
    }
}

void MX_USART2_UART_Init(void)
{
    __HAL_RCC_GPIOA_CLK_ENABLE();

    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_15;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    huart2.Instance = USART2;
    huart2.Init.BaudRate = 115200;
    huart2.Init.WordLength = UART_WORDLENGTH_8B;
    huart2.Init.StopBits = UART_STOPBITS_1;
    huart2.Init.Parity = UART_PARITY_NONE;
    huart2.Init.Mode = UART_MODE_TX_RX;
    huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart2.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart2) != HAL_OK)
    {
        Error_Handler("Falha na Inicialização da UART 2");
    }

    __HAL_RCC_USART2_CLK_ENABLE();
}

#define STDOUT_FILENO   1
#define STDERR_FILENO   2

int _write(int file, char *ptr, int len)
{
    switch (file)
    {
        case STDOUT_FILENO:
            HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
            break;
        case STDERR_FILENO:
            HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY);
            break;
        default:
            return -1;
    }
    return len;
}

void Error_Handler(const char *error)
{
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, GPIO_PIN_SET);
    printf("Erro: %s\r\n", error);
    for(;;);
}

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
    HAL_SYSTICK_Config(SystemCoreClock /1000);
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0);
    return HAL_OK;
}

static inline uint32_t LL_SYSTICK_IsActiveCounterFlag(void)
{
    return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
}

uint32_t getCurrentMicros(void)
{
    LL_SYSTICK_IsActiveCounterFlag();
    uint32_t m = HAL_GetTick();
    const uint32_t tms = SysTick->LOAD + 1;
    __IO uint32_t u = tms - SysTick->VAL;

    if (LL_SYSTICK_IsActiveCounterFlag()) {
        m = HAL_GetTick();
        u = tms - SysTick->VAL;
    }

    return (m * 1000 + (u * 1000) / tms);
}