/*
 * main.c
 *
 *  Created on: Jan 3, 2023
 *      Author: Tjitte van der Ploeg
 */


#include "stm32g4xx_hal.h"
#include "delay.h"
#include "buffer.h"
#include "flash.h"
#include "crc.h"

#define LED0_Pin 			GPIO_PIN_15
#define LED0_GPIO_Port 		GPIOC
#define LED1_Pin 			GPIO_PIN_14
#define LED1_GPIO_Port 		GPIOC
#define LED2_Pin 			GPIO_PIN_13
#define LED2_GPIO_Port 		GPIOC
#define LED3_Pin 			GPIO_PIN_9
#define LED3_GPIO_Port 		GPIOB


#define LED_Pin LED0_Pin
#define LED_GPIO_Port LED0_GPIO_Port


void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
void jumpToMainApplication(void);

#define NEW_APP_MAX_SIZE (APP_BASE - MAIN_BASE)

uint8_t   bootloaderStateCurrent;
uint8_t   bootloaderStateNext;
uint32_t  bootloaderDelayLastTick;
uint32_t  bootloaderDelayTime;
uint8_t*  newAppAdress;
uint32_t  newAppSize;
uint32_t  newAppCRC;
int32_t   indexPointer;
uint8_t   yAxisOffset;

typedef enum {
	BOOT_INIT = 0,
	BOOT_DELAY,
	BOOT_SIZE_CHECK,
	BOOT_SIZE_ZERO,
	BOOT_SIZE_WRONG,
	BOOT_SIZE_OK,
	BOOT_CRC_CHECK,
	BOOT_CRC_OK,
	BOOT_CRC_ERROR,
	BOOT_ERASE,
	BOOT_ERASE_ERROR,
	BOOT_ERASE_SUCCES,
	BOOT_COPYAPP,
	BOOT_COPYAPP_ERROR,
	BOOT_COPYAPP_SUCCES,
	BOOT_DONE,
	BOOT_REBOOT,
	BOOT_ERROR
} bootLoaderState;

int main(void) {
	HAL_Init();
	SystemClock_Config();
	MX_GPIO_Init();

	modDelayInit();

	//driverHWPowerStateInit();
	//driverHWPowerStateSetOutput(P_STAT_POWER_ENABLE,P_STAT_SET);

	newAppAdress           = (uint8_t*)APP_BASE;
	indexPointer           = 0;
	newAppSize             = buffer_get_uint32(newAppAdress, &indexPointer);
	newAppCRC              = buffer_get_uint16(newAppAdress, &indexPointer);

	indexPointer += 2;	//2 dummy butes to alaign memory with 8 bytes , dword

	bootloaderStateCurrent = BOOT_INIT;
	bootloaderStateNext    = BOOT_INIT;
	bootloaderDelayTime    = 100;

	//emptyData.Empty = 0.0f;

	bootloaderDelayLastTick = HAL_GetTick();

  while (1) {

		switch(bootloaderStateCurrent) {
			case BOOT_INIT:
			  bootloaderDelayTime    = 100;
			  bootloaderStateCurrent = BOOT_DELAY;
			  bootloaderStateNext    = BOOT_SIZE_CHECK;
			  break;

			case BOOT_DELAY:
				if(modDelayTick1ms(&bootloaderDelayLastTick,bootloaderDelayTime))
					bootloaderStateCurrent = bootloaderStateNext;
				break;

			case BOOT_SIZE_CHECK:
				if(newAppSize == 0)
					bootloaderStateNext = BOOT_SIZE_ZERO;
				else if(newAppSize > NEW_APP_MAX_SIZE)
					bootloaderStateNext = BOOT_SIZE_WRONG;
				else
					bootloaderStateNext = BOOT_SIZE_OK;

			  bootloaderDelayTime    = 100;
			  bootloaderStateCurrent = BOOT_DELAY;
			  break;

			case BOOT_SIZE_ZERO:
				bootloaderStateNext    = BOOT_ERROR;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_SIZE_WRONG:
				bootloaderStateNext    = BOOT_ERROR;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_SIZE_OK:
				bootloaderStateNext    = BOOT_CRC_CHECK;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_CRC_CHECK:
				if(newAppCRC != libCRCCalcCRC16(newAppAdress + indexPointer, newAppSize))
					bootloaderStateNext = BOOT_CRC_ERROR;
				else
					bootloaderStateNext = BOOT_CRC_OK;

				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_CRC_OK:
				bootloaderStateNext    = BOOT_ERASE;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_CRC_ERROR:
				bootloaderStateNext    = BOOT_ERROR;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_ERASE:
				// Code to erasing main flash
				if(modFlashEraseMainAppData(newAppSize) == HAL_OK)
					bootloaderStateNext    = BOOT_ERASE_SUCCES;
				else
					bootloaderStateNext    = BOOT_ERASE_ERROR;

				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_ERASE_ERROR:
				bootloaderStateNext    = BOOT_ERROR;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_ERASE_SUCCES:
				bootloaderStateNext    = BOOT_COPYAPP;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;

			case BOOT_COPYAPP:
				// Code to copy new app to main flash
				if(modFlashCopyNewAppToMainApp((uint64_t*)(newAppAdress + indexPointer), newAppSize) == HAL_OK)
					bootloaderStateNext    = BOOT_COPYAPP_SUCCES;
				else
					bootloaderStateNext    = BOOT_COPYAPP_ERROR;

				bootloaderDelayTime    = 100;
			  bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_COPYAPP_ERROR:
				bootloaderStateNext    = BOOT_ERROR;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_COPYAPP_SUCCES:
				bootloaderStateNext    = BOOT_DONE;
				bootloaderDelayTime    = 100;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_DONE:
				bootloaderStateNext    = BOOT_REBOOT;
				bootloaderDelayTime    = 2000;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			case BOOT_REBOOT:
				jumpToMainApplication();
				break;
			case BOOT_ERROR:
				HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, 1);

				// Do nothing but keep showing error.
				bootloaderStateNext    = BOOT_REBOOT;
				bootloaderDelayTime    = 5000;
				bootloaderStateCurrent = BOOT_DELAY;
				break;
			default:
				break;
		}
	};
}

void jumpToMainApplication(void) {
	NVIC_SystemReset();
}

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

	// configure the main internal regulator output voltage
	HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1_BOOST);
	/** Initializes the RCC Oscillators according to the specified parameters
	 * in the RCC_OscInitTypeDef structure.
	 */
	RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
	RCC_OscInitStruct.HSEState = RCC_HSE_ON;
	RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
	RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
	RCC_OscInitStruct.PLL.PLLM = RCC_PLLM_DIV4;
	RCC_OscInitStruct.PLL.PLLN = 85;
	RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
	RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
	RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
	if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
		Error_Handler();
	}
	/** Initializes the CPU, AHB and APB buses clocks
	 */
	RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
			| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
	RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
	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_4) != HAL_OK) {
		Error_Handler();
	}
	/** Initializes the peripherals clocks
	 */
	PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART3
			| RCC_PERIPHCLK_ADC12 | RCC_PERIPHCLK_ADC345 | RCC_PERIPHCLK_FDCAN;
	PeriphClkInit.Usart3ClockSelection = RCC_USART3CLKSOURCE_PCLK1;
	PeriphClkInit.FdcanClockSelection = RCC_FDCANCLKSOURCE_PCLK1;
	PeriphClkInit.Adc12ClockSelection = RCC_ADC12CLKSOURCE_SYSCLK;
	PeriphClkInit.Adc345ClockSelection = RCC_ADC345CLKSOURCE_SYSCLK;
	if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
		Error_Handler();
	}
}

static void MX_GPIO_Init(void) {
	__HAL_RCC_GPIOC_CLK_ENABLE();
	__HAL_RCC_GPIOF_CLK_ENABLE();
	__HAL_RCC_GPIOG_CLK_ENABLE();
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_GPIOB_CLK_ENABLE();

	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.Pin = LED_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	HAL_GPIO_Init(LED_GPIO_Port, &GPIO_InitStruct);
}

void Error_Handler(void) {
  while(1){};
}

void NMI_Handler(void) {

}

void HardFault_Handler(void) {
	while (1) {

	}
}

void MemManage_Handler(void) {

	while (1) {

	}
}

void BusFault_Handler(void) {

	while (1) {

	}
}

void UsageFault_Handler(void) {

	while (1) {

	}
}

void SVC_Handler(void) {

}

void DebugMon_Handler(void) {

}

void PendSV_Handler(void) {

}

void SysTick_Handler(void) {
	HAL_IncTick();
}

