/**
 * @file init.c
 * @brief see header file for detailed description
 *
 * @par 
 *    Project: Evolaris 
 *    Repository Path: TOCOMMENT: enter the repository path
 * @copyright evolaris, CH-2560 Nidau
 *
 * @date  date of creation: 16.12.2016
 *
 * @author  Patrick Haldi
 *
 */

#include "init.h"              //own module header

#include "stm32f1xx_hal.h"

#include <string.h>

#include "app/init/build_defs.h"
#include "app/config/cfg_sys.h"
#include "app/slave_module_id/slave_module_id.h"

#include "core/ca_common/param.h"
#include "core/ca_common/ca_disp.h"
#include "core/ca_common/translator.h"
#include "core/afe/afe_bq769x0.h"
#include "core/temp_sensor/temp_sensor.h"
#include "core/can_msg_buffer/can_msg_buffer.h"
#include "core/debug/debug.h"
#include "core/balancing/balancing.h"
#include "core/time_provider/time_provider.h"

#include "hal/hal_include/gpio_pins.h"
#include "hal/hal_include/uart.h"
#include "hal/hal_include/time_provider_timer.h"
#include "hal/hal_include/i2c.h"
#include "hal/hal_include/can.h"
#include "hal/hal_include/eeprom_mgr.h"
#include "hal/adc_config/adc_channels.h"
#include "hal/sys_tick/sys_tick.h"
#include "hal/critical_section/critical_section.h"
#include "hal/hal_include/fault_handling.h"

#include "util/misc/printf-stdarg.h"
#include "util/misc/delay.h"

/*----------------------------------------------------------------------------
    local constants and macros
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
    local types  and enums
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
   declaration of local (static) functions
----------------------------------------------------------------------------*/

/**
 * inits the function pointers
 *
 * @param  none
 *
 * @return none
 */
static void init_fnc_pointers(void);

/**
 * inits the hal functions supplied by stm
 *
 * @param  none
 *
 * @return none
 */
static void init_hal_stm(void);

/**
 * inits the internal clock
 *
 * @param  none
 *
 * @return none
 */
static void SystemClock_Config(void);

/**
 * inits the hal layer
 *
 * @param  none
 *
 * @return none
 */
static void init_hal(void);

/**
 * inits the core layer
 *
 * @param  p_s_bq769x0  pointer to the bq-structure
 *
 * @return none
 */
static void init_core(S_bq769x0_handle *p_s_bq769x0);

#if USE_UART == true
/**
 * sends a boot-up message over the char stream
 *
 * @param  none
 *
 * @return none
 */
static void send_boot_msg(void);
#endif

#if USE_CAN == true
/**
 * sends the current software version once over the CAN bus to the master module
 *
 * @param  none
 *
 * @return none
 */
static void send_sw_version(void);
#endif

/**
 * blinks both leds in a quick sequence to indicate start
 *
 * @param  none
 *
 * @return none
 */
static void _led_blink_sequence(void);

/*----------------------------------------------------------------------------
   module scope (static) variables
----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------
   implementation of global functions
----------------------------------------------------------------------------*/

void init(S_bq769x0_handle *p_s_bq769x0)
{

    init_fnc_pointers();
	init_hal_stm();
    init_hal();
    init_core(p_s_bq769x0);
#if USE_UART == true
    //send_boot_msg();
#endif
    _led_blink_sequence();
    sys_tick_start();
#if USE_CAN == true
    can_rx_start(CAN_S_SMOD);
    send_sw_version();
#endif
}

/*----------------------------------------------------------------------------
   implementation of local (static) functions
----------------------------------------------------------------------------*/

static void init_fnc_pointers(void)
{
    time_provider_register(         (time_provider_t)&time_provider_timer_get_time, time_provider_timer_get_overflow_time());

    register_delay_ms_fnc(          &time_provider_delay_ms);
    register_delay_us_fnc(          &time_provider_delay_us);
    register_delay_ms_until_fnc(    &time_provider_delay_until_ms);
}

static void init_hal_stm(void)
{
    HAL_Init();
    SystemClock_Config();
    //SystemInit();

// TODO 2 use?
        //HAL_RCC_ClockConfig()
}


void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct;
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
    RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks
     */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = 16;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL4;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        report_error("osc init");
    }

    /**Initializes the CPU, AHB and APB busses 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_0) != HAL_OK)
    {
        report_error("clk init");
    }

    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
    {
        report_error("periph init");
    }

    /**Configure the Systick interrupt time
     */
    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / 1000);

    /**Configure the Systick
     */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

    /* SysTick_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
//static void SystemClock_Config(void)
//{
//
//    RCC_OscInitTypeDef RCC_OscInitStruct;
//    RCC_ClkInitTypeDef RCC_ClkInitStruct;
//    RCC_PeriphCLKInitTypeDef PeriphClkInit;
//
//      /**Initializes the CPU, AHB and APB busses clocks
//      */
//    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
//    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
//    RCC_OscInitStruct.HSICalibrationValue = 16;
//    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
//    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
//    {
//      Error_Handler();
//    }
//
//      /**Initializes the CPU, AHB and APB busses clocks
//      */
//    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();
//    }
//
//    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
//    PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV2;
//    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
//    {
//      Error_Handler();
//    }
//
//      /**Configure the Systick interrupt time
//      */
//    HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
//
//      /**Configure the Systick
//      */
//    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
//
//    /* SysTick_IRQn interrupt configuration */
//    HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
//}

static void init_hal(void)
{
    test_isotypes();
    time_provider_timer_init();
    gpio_pins_init();
    gpio_pin_set_value(IO_LED_ERROR, false);
    gpio_pin_set_value(IO_LED_RUN, false);
    adc_init();

#if USE_I2C == true
    i2c_init(BQ_I2C_IF, I2C_BAUD_100k);
#endif

#if USE_UART == true
    uart_init(UART_0, SYS_CFG_SERIAL_BAUD, SYS_CFG_SERIAL_NR_STOPBITS); // TODO 2: maybe put to start
    uart_set_as_char_stream_output(UART_0);
    char_stream_clear();
#endif

    sys_tick_init();


    // TODO 2: bring hibernation module to run:
//    SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);
//    HibernateEnableExpClk(SysCtlClockGet()); // set clock to system clock
//    HibernateGPIORetentionDisable();

}

static void init_core(S_bq769x0_handle *p_s_bq769x0)
{
    slave_module_id_init();
#if USE_I2C == true
    afe_reset(p_s_bq769x0);
    afe_init(p_s_bq769x0, BQ_I2C_ID);
#endif
    temp_sensors_init();
    paramInit();
    //InitError();
#if USE_CAN == true
    //CA_NSHInit();
    can_bus_init(CAN_BUS_01, CA_INTF0_BAUD); // bus need to be initialized but not used by CADisp
    CADispInit();
    paramSetCANIDOffset(slave_module_id_get());
    paramSetNodeID(18); // TODO 0: make more elegant
#endif
}

#if USE_UART == true
static void send_boot_msg(void)
{
    char_t s_boot_str[] = "Slave module #00 booted ~~~~~~~~~~~~";

    sprintf_reentrant(s_boot_str, "Slave module #%2d booted ~~~~~~~~~~~~", slave_module_id_get());
    char_stream_send_string(s_boot_str);
    char_stream_send_newline();
}
#endif

#if USE_CAN == true
static void send_sw_version(void)
{
    const SParamGlobDef* p = paramIDgetGlobalIDs();

    const uint32_t u32_year = BUILD_YEAR - 2000u;
    const uint32_t u32_month = BUILD_MONTH;
    const uint32_t u32_day = BUILD_DAY;
    const uint32_t u32_date_number = u32_year * 10000u + u32_month * 100u + u32_day;
    paramSet_u32(&p->ESD_SMOD.uSW_DATE, u32_date_number);
    paramSendMessage2CAN(MSG_ESD_SMOD_USW_DATE, CAN_SMOD0);

// TODO 0 bring back the time
//    const EParamIDDef e_sw_time_param = (EParamIDDef)(PAID_ESD_SMOD_SW_TIME_0 + slave_module_id_get());
//    const uint32_t u32_hour = BUILD_HOUR;
//    const uint32_t u32_minute = BUILD_MIN;
//    const uint32_t u32_second = BUILD_SEC;
//    const uint32_t u32_time_number = u32_hour * 10000u + u32_minute * 100u + u32_second;
//    paramWrite_u32(e_sw_time_param, u32_time_number);
//    paramSendToCAM(e_sw_time_param, EINTERF_CAN_SMOD);
}
#endif

#define NR_OF_BLINKS 20u
#define BLINK_DURATION_MS 50u

static void _led_blink_sequence(void)
{
    uint32_t i;

    bool_t b_green_led_on = false;

    for(i = 0u; i < NR_OF_BLINKS; i++)
    {
        gpio_pin_set_value(IO_LED_RUN, b_green_led_on);
        gpio_pin_set_value(IO_LED_ERROR, !b_green_led_on);
        b_green_led_on = !b_green_led_on;
        delay_ms(BLINK_DURATION_MS);
    }
}
