/* Copyright (c) 2013 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

/**@file
 *
 * @defgroup ble_sdk_app_bootloader_main main.c
 * @{
 * @ingroup dfu_bootloader_api
 * @brief Bootloader project main file.
 *
 * -# Receive start data packet. 
 * -# Based on start packet, prepare NVM area to store received data. 
 * -# Receive data packet. 
 * -# Validate data packet.
 * -# Write Data packet to NVM.
 * -# If not finished - Wait for next packet.
 * -# Receive stop data packet.
 * -# Activate Image, boot application.
 *
 */
#include "dfu_transport.h"
#include "bootloader.h"
#include "bootloader_util.h"
#include <stdint.h>
#include <string.h>
#include <stddef.h>
#include "nordic_common.h"
#include "nrf.h"
#include "nrf_soc.h"
#include "app_error.h"
#include "nrf_gpio.h"
#include "ble.h"
#include "nrf.h"
#include "ble_hci.h"
#include "app_scheduler.h"
#include "app_timer_appsh.h"
#include "nrf_error.h"
#include "bsp.h"
#include "softdevice_handler_appsh.h"
#include "pstorage_platform.h"
#include "nrf_mbr.h"
#include "nrf_log.h"
#include "nrf_delay.h"

#if BUTTONS_NUMBER < 1
#error "Not enough buttons on board"
#endif

#if LEDS_NUMBER < 1
#error "Not enough LEDs on board"
#endif

#define IS_SRVC_CHANGED_CHARACT_PRESENT 1                                                       /**< Include the service_changed characteristic. For DFU this should normally be the case. */

//#define BOOTLOADER_BUTTON               BSP_BUTTON_3                                            /**< Button used to enter SW update mode. */
//#define UPDATE_IN_PROGRESS_LED          BSP_LED_2                                               /**< Led used to indicate that DFU is active. */

#define APP_TIMER_PRESCALER             0                                                       /**< Value of the RTC1 PRESCALER register. */
#define APP_TIMER_OP_QUEUE_SIZE         4                                                       /**< Size of timer operation queues. */

#define SCHED_MAX_EVENT_DATA_SIZE       MAX(APP_TIMER_SCHED_EVT_SIZE, 0)                        /**< Maximum size of scheduler events. */

#define SCHED_QUEUE_SIZE                20                                                      /**< Maximum number of events in the scheduler queue. */


/**@brief Callback function for asserts in the SoftDevice.
 *
 * @details This function will be called in case of an assert in the SoftDevice.
 *
 * @warning This handler is an example only and does not fit a final product. You need to analyze 
 *          how your product is supposed to react in case of Assert.
 * @warning On assert from the SoftDevice, the system can only recover on reset.
 *
 * @param[in] line_num    Line number of the failing ASSERT call.
 * @param[in] file_name   File name of the failing ASSERT call.
 */
void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
{
    app_error_handler(0xDEADBEEF, line_num, p_file_name);
}


/**@brief Function for initialization of LEDs.
 */
#if 0
static void leds_init(void)
{
    nrf_gpio_range_cfg_output(LED_START, LED_STOP);
    nrf_gpio_pins_set(LEDS_MASK);
}
#endif


/**@brief Function for initializing the timer handler module (app_timer).
 */
static void timers_init(void)
{
    // Initialize timer module, making it use the scheduler.
    APP_TIMER_APPSH_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, true);
}


/**@brief Function for initializing the button module.
 */
#if 0
static void buttons_init(void)
{
    nrf_gpio_cfg_sense_input(BOOTLOADER_BUTTON,
                             BUTTON_PULL, 
                             NRF_GPIO_PIN_SENSE_LOW);

}
#endif

/**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 *
 * @details This function is called from the scheduler in the main loop after a BLE stack
 *          event has been received.
 *
 * @param[in]   p_ble_evt   Bluetooth stack event.
 */
static void sys_evt_dispatch(uint32_t event)
{
    pstorage_sys_event_handler(event);
}


/**@brief Function for initializing the BLE stack.
 *
 * @details Initializes the SoftDevice and the BLE event interrupt.
 *
 * @param[in] init_softdevice  true if SoftDevice should be initialized. The SoftDevice must only 
 *                             be initialized if a chip reset has occured. Soft reset from 
 *                             application must not reinitialize the SoftDevice.
 */
static void ble_stack_init(bool init_softdevice)
{
    uint32_t         err_code;
    sd_mbr_command_t com = {SD_MBR_COMMAND_INIT_SD, };
    nrf_clock_lf_cfg_t clock_lf_cfg = NRF_CLOCK_LFCLKSRC;

    if (init_softdevice)
    {
        err_code = sd_mbr_command(&com);
        APP_ERROR_CHECK(err_code);
    }
    
    err_code = sd_softdevice_vector_table_base_set(BOOTLOADER_REGION_START);
    APP_ERROR_CHECK(err_code);
   
    SOFTDEVICE_HANDLER_APPSH_INIT(&clock_lf_cfg, true);

    // Enable BLE stack.
    ble_enable_params_t ble_enable_params;
    // Only one connection as a central is used when performing dfu.
    err_code = softdevice_enable_get_default_config(1, 1, &ble_enable_params);
    APP_ERROR_CHECK(err_code);

    ble_enable_params.gatts_enable_params.service_changed = IS_SRVC_CHANGED_CHARACT_PRESENT;
    err_code = softdevice_enable(&ble_enable_params);
    APP_ERROR_CHECK(err_code);
    
    err_code = softdevice_sys_evt_handler_set(sys_evt_dispatch);
    APP_ERROR_CHECK(err_code);
}


/**@brief Function for event scheduler initialization.
 */
static void scheduler_init(void)
{
    APP_SCHED_INIT(SCHED_MAX_EVENT_DATA_SIZE, SCHED_QUEUE_SIZE);
}



#if 1 // Lumen Labs

//#include "PCB_Ultra.h"
//#include "PCB_V0p4.h"
  #include "PCB_V1p0220923.h"



bool is_button_pressed(void) {
    if(LL_PIN_Y__POWER_BUTTON == nrf_gpio_pin_read(LL_PIN__POWER_BUTTON)) {
        return true; 
    } else {
        return false;
    }
}

unsigned long gulResetReason;
bool is_reset_by_button(void) {
    if( 1 == ((gulResetReason & (0x1<<16)) >> 16) ) { // if reset by key (or by reset pin when key pressed)
        #if 0 // can not return true directly, since it maybe due to reset pin when key pressed
            return true; 
        #else
            if(0x1 == (gulResetReason&0x1)) { // if reset by reset pin
                return false;
            } else { // else, must be by key really
                return true; 
            }
        #endif
    } else { // else, this reset not due to key
        return false;
    }
}



unsigned long isOnTheCharger(void) {
    return (LL_PIN_Y__CHARGING_5V == nrf_gpio_pin_read(LL_PIN__CHARGING_5V));
}



// Power off when button pressed.
// Should only work when there is DFU "Idle" state, but Nordic has no such state.
static unsigned long debounce_cnt = 0;
static unsigned long prev_button_state = 0xFFFFFFFF; // 0xFFFFFFFF: wait a released state.
void power_off_when_button_pressed(void) {
    unsigned long  now_button_state = is_button_pressed() ? LL_PIN_Y__POWER_BUTTON : LL_PIN_N__POWER_BUTTON;
    if(0xFFFFFFFF == prev_button_state) { // wait a released button state, to start this routine
        if(LL_PIN_N__POWER_BUTTON == now_button_state) { // button released
            prev_button_state = LL_PIN_N__POWER_BUTTON;
            debounce_cnt = 0;
        } else { /* keep waiting button released */ }
    } else { // power off when button pressed
        if(prev_button_state != now_button_state) {
            if(debounce_cnt > 3) {
//                LL_PIN_N__PWR_OF_LED(LL_PIN__PWR_OF_LED);
//                LL_PIN_N__LED(LL_PIN__LED_YELLOW_L);
//                LL_PIN_N__LED(LL_PIN__LED_YELLOW_R);
                LL_PIN_N__LED(LL_PIN__LED_1);
                while(LL_PIN_Y__POWER_BUTTON == (is_button_pressed() ? LL_PIN_Y__POWER_BUTTON : LL_PIN_N__POWER_BUTTON)); // wait button release.
                sd_power_system_off();
            } else { /* keep waiting debounce timeout */ }
        } else { debounce_cnt = 0; }
    }
}



APP_TIMER_DEF(m_rescue_mode_indicate_timer_id);
static void rescue_mode_indicate_timeout_handler(void * p_context) {
//    nrf_gpio_pin_toggle(LL_PIN__LED_BRIM_RED);
//    nrf_gpio_pin_toggle(LL_PIN__LED_WHITE_R);    
    nrf_gpio_pin_toggle(LL_PIN__LED_1);
    debounce_cnt++;
}



#endif



/**@brief Function for bootloader main entry.
 */
int main(void)
{
    uint32_t err_code;
    bool     dfu_start = false;
    bool     app_reset = (NRF_POWER->GPREGRET == BOOTLOADER_DFU_START);

    gulResetReason = NRF_POWER->RESETREAS; NRF_POWER->RESETREAS = gulResetReason;//0xFFFFFFFF; 
    
    if (app_reset)
    {
        NRF_POWER->GPREGRET = 0;
    }
   
    nrf_gpio_cfg_output(LL_PIN__LED_1);      LL_PIN_N__LED(LL_PIN__LED_1);

    
    
    // This check ensures that the defined fields in the bootloader corresponds with actual
    // setting in the chip.
    APP_ERROR_CHECK_BOOL(*((uint32_t *)NRF_UICR_BOOT_START_ADDRESS) == BOOTLOADER_REGION_START);
    APP_ERROR_CHECK_BOOL(NRF_FICR->CODEPAGESIZE == CODE_PAGE_SIZE);

    // Initialize.
    timers_init();
//  buttons_init();
    nrf_gpio_cfg_sense_input(LL_PIN__POWER_BUTTON, LL_PULL_MODE__POWER_BUTTON, LL_TRIGGER_MODE__POWER_BUTTON);
    nrf_gpio_cfg_sense_input(LL_PIN__CHARGING_5V, LL_PULLMODE__CHARGING_5V, NRF_GPIO_PIN_NOSENSE);

    (void)bootloader_init();

    if (bootloader_dfu_sd_in_progress())
    {
//      nrf_gpio_pin_clear(UPDATE_IN_PROGRESS_LED);

        err_code = bootloader_dfu_sd_update_continue();
        APP_ERROR_CHECK(err_code);

        ble_stack_init(!app_reset);
        scheduler_init();

        err_code = bootloader_dfu_sd_update_finalize();
        APP_ERROR_CHECK(err_code);

//      nrf_gpio_pin_set(UPDATE_IN_PROGRESS_LED);
    }
    else
    {
        // If stack is present then continue initialization of bootloader.
        ble_stack_init(!app_reset);
        scheduler_init();
    }

    dfu_start  = app_reset;
//  dfu_start |= ((nrf_gpio_pin_read(BOOTLOADER_BUTTON) == 0) ? true: false);
    
    
    
    if (dfu_start || (!bootloader_app_is_valid(DFU_BANK_0_REGION_START)))
    {
        LL_PIN_Y__LED(LL_PIN__LED_1);
        // Create timer
        err_code = app_timer_create(&m_rescue_mode_indicate_timer_id, APP_TIMER_MODE_REPEATED, rescue_mode_indicate_timeout_handler);
        APP_ERROR_CHECK(err_code);
        // Start timer
        err_code = app_timer_start(m_rescue_mode_indicate_timer_id, APP_TIMER_TICKS(1000, APP_TIMER_PRESCALER), NULL);
        APP_ERROR_CHECK(err_code);

        // Initiate an update of the firmware.
        err_code = bootloader_dfu_start();
        APP_ERROR_CHECK(err_code);

//      nrf_gpio_pin_set(UPDATE_IN_PROGRESS_LED);
    }

    if (bootloader_app_is_valid(DFU_BANK_0_REGION_START) && !bootloader_dfu_sd_in_progress())
    {
        // Select a bank region to use as application region.
        // @note: Only applications running from DFU_BANK_0_REGION_START is supported.
        bootloader_app_start(DFU_BANK_0_REGION_START);
    }
    
    NVIC_SystemReset();
}
