/*
 * Sleep.c
 *
 * Suspend and hibernation functions
 *
 *  Created on: Jan 24, 2014
 *      Author: rob
 *  Modified Apr 2018, Thom Maughan
 */

#include "system.h"
#include "sleep.h"
#include "user_io.h"
//#include "uartstdio.h"	// User local version with larger RX buffer
#include "microsd.h"
#include "init.h"
#include "board_util.h"
#include "uartstdio.h"

extern struct systemSamplingData sys_samp;

extern uint32_t storeSysDataVariables(void);

extern int dbg_flag;  // main.c


void sleep_debug(void);


/*
 * It is desired to catch the BOR1 or BOR0 interrupt
 * PBORCTL has a bit that enables an interrupt or a reset for BOR1 (2.88) and BOR0 (3.02)
 *
 * SysCtlResetCauseGet
 *
 * Configure the Brownout reset
 * SysCtlBrownOutConfigSet(SYSCTL_INT_BOR,0);
 * SysCtlBrownOutConfigSet(SYSCTL_INT_BOR0,0);
 * SysCtlBrownOutConfigSet(SYSCTL_INT_BOR1,0);
 * Enable the Brownout interrupt in sysctl
 * SysCtlIntEnable(SYSCTL_INT_BOR1)
 *
 * What is the ISR vector??
 */

//
// The following are values that can be passed to the SysCtlBrownOutConfigSet()
// API as the ui32Config parameter.
//
//
//#define SYSCTL_BOR_RESET        0x00000002  // Reset instead of interrupting
//#define SYSCTL_BOR_RESAMPLE     0x00000001  // Resample BOR before asserting

/*
 * sleep()
 * Puts controller in hibernate mode. Controller will wake by either keystroke or real-time clock
 * alarm match. Action after wake determined by state argument (IDLE or DEPLOYED). IDLE state returns
 * to main menu. DEPLOYED state returns to sample loop.
 * RCG 4/14
 */
void sleep(deploy_state state, uint32_t wake_time)
{
	uint32_t ulStatus;
	time_t current_time;



	sys_samp.state = state;     // Save the current state for when we wakeup

	// THOM DEBUG
	//uprintf( "\r\ncurrent time: %u",ROM_HibernateRTCGet() );
	//uprintf("\r\nwakeup: %u, fptr: %u", sys_data.nextWakeUp, sys_data.next_gdata_fptr);
	//ROM_SysCtlDelay(MILLISECOND*100);

	if(state == DEPLOYED)
	{
	    if(wake_time > 0)   // Polled Mode is wake_time=0
	    {
            // Check if wake time is in past to prevent lockup
            current_time = ROM_HibernateRTCGet();

            //if(wake_time <=  current_time + 2)	// Wake time too soon or in the past (corrupt)  // 15 Aug 2022: Wirewalker use case might need if((wake_time <=  current_time + 2) || (wake_time > current_time + 'a day or so'))
            // Check for corrupt wakeup.     // 15 Aug 2022: Wirewalker samples at 2 seconds, power gets turned off.   Bug, it stopped sampling after a week.   Hypothesis is wakeup time was corrupted into the future.
            if((wake_time <= current_time + MIN_SAMPLING_PERIOD) || (wake_time > current_time + MAX_SAMPLING_PERIOD))
            {
                // THOM DEBUG
                //uprintf("\r\n\r\nWake time %u is <= current time %u\r\n", wake_time, current_time);
                //ROM_SysCtlDelay(MILLISECOND*25);
                wake_time = current_time + 2;	// Add two seconds, results in a wakeup in 2 seconds which then will take a sample, then re compute wakeup to fix.
            }

            // Set wake time
            HibernateRTCMatchSet(0, wake_time);

            //Set wake condition on wake pin
            ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN | HIBERNATE_WAKE_RTC);

            //uprintf("\r\n\r\nDebug in sleep.c line 70, Sleeping until ");
            //print_time_t_Time(wake_time);

            if(sys_data.test_mode == 1 || sys_data.output == VERBOSE)
            {
                uprintf("\r\n\r\nSleeping until ");
                print_time_t_Time(wake_time);
            }
	    } // if wake_time >0
	    else
	    {
	        // Polled mode (RTC is not used to wake)
	        ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN);
	        uprintf("\r\n\r\nSleeping in polled mode..\r\n\r\n\r\n");
	    }
	}

	//else if(state == IDLE)
	else // state IDLE or COMMAND           THOM 8 May 2018  THOM Polled Mode
	{
        ROM_HibernateWakeSet(HIBERNATE_WAKE_PIN);
        if(state == IDLE)
            uprintf("\r\n\r\nSleeping...*");

		if(sys_data.output == VERBOSE)
		    uprintf("hit any key to wake\r\n\r\n\r\n");

        ROM_SysCtlDelay(MILLISECOND*25);
	}

	UARTFlushTx(0);

	// Save our sys_data variables before hibernating!  EEPROM has endurance of 500K Program/Erase cycles.
	// 500,000 seconds is 5.8 days => 8 second sampling interval is 46 days and the EEPROM is at it's limit
	// Copy the sys_data struct into EEPROM.  Last argument ensures # of bytes is multiple of 4

	// TODO, check side effects of not storing sys_data
	// Need utilities to dump sys_data, compare, etc.
	if(storeSysSampFRAMVariables())
	{

	}
#ifdef THISWILLWEAROUTEEPROM
	if(storeSysDataVariables())     //if(EEPROMProgram((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3))
	{
		uprintf("Error storing system data (sleep.c).\r\n");   // NOTE: this wont print due to console_off() above
		wait_consoleTx();
		error_store("Error storing system data (sleep.c).");
		return;
	}
#endif

	console_off();  // THOM new location 24 Sep 2019

//	sleep_debug();      // THOM DEBUG - THIS SHOWS WR_COMPLETE interrupt is active

	// Clear any pending interrupts
	ulStatus = ROM_HibernateIntStatus(0);
	ROM_HibernateIntClear(ulStatus);

	ROM_HibernateRTCTrimSet (0x7FFF);	// Make sure trim is set to default to prevent lockup (see errata)

	// Tell hibernation module we want to sleep
	ROM_HibernateRequest();

    ROM_SysCtlDelay(100*MILLISECOND);    // Give time to hibernate.

    // NOTE: code should never get here....
    console_on();  // THOM new 5 Aug 2020 - debugging brds that dont sleep

    // Shouldn't get to this point, something went wrong - Typically it is due to running MFET compiled code on the SEAPHOX
    uprintf("\r\n\r\n\r\nHibernation error!  Possible cause: remove JP1 on MFET board \r\n\r\n\r\n");
    error_store("Hibernation error.  Possible cause: remove JP1 on MFET board.  \r\n");

    uprintf("\r\n\r\n\r\nCalling SysCtrlReset...\r\n\r\n\r\n");
    ROM_SysCtlDelay(100*MILLISECOND);

    // Code should never get here, but just in case, reset
    ROM_SysCtlReset();
}



void sleep_debug(void)
{
    uint32_t uiStatus;

    uiStatus = ROM_HibernateIntStatus(false);   // Read the status to determine cause of wake.
    ROM_HibernateIntClear(uiStatus);            // Clear the hibernate interrupt

    // Wake pin
    if(uiStatus & HIBERNATE_INT_PIN_WAKE)           // 0x00000008
    {
        //uprintf("\r\nWake by pin");       // THOM DEBUG (comment this line out)
        if(sys_samp.state == DEPLOYED)  // User hit key during sleep while deployed, enter command mode.
        {
            //uprintf(" - state = DEPLOYED ");       // THOM DEBUG (comment this line out)
            sys_samp.state = COMMAND;
        }

    }
    else if(uiStatus & HIBERNATE_INT_RTC_MATCH_0)   // 0x00000001  // RTC match, Take a sample
    {
        //uprintf("\r\nWake by RTC match");     // THOM DEBUG (comment this line out)
        sys_samp.state = DEPLOYED;
    }
    else if(uiStatus & HIBERNATE_INT_VDDFAIL)       // 0x00000080
    {
        //uprintf("\r\nWake by HIB INT VDDFAIL");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }

    else if(uiStatus & HIBERNATE_INT_RESET_WAKE)        // 0x00000040
    {
        //uprintf("\r\nWake by HIB INT RESET WAKE");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
    else if(uiStatus & HIBERNATE_INT_GPIO_WAKE)         // 0x00000020
    {
        //uprintf("\r\nWake by HIB INT GPIO WAKE");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
    else if(uiStatus & HIBERNATE_INT_WR_COMPLETE)       // 0x00000010
    {
        //uprintf("\r\nWake by HIB INT WR COMPLETE");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
    else if(uiStatus & HIBERNATE_INT_PIN_WAKE)          // 0x00000008
    {
        //uprintf("\r\nWake by HIB INT PIN WAKE");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
    else if(uiStatus & HIBERNATE_INT_LOW_BAT)           // 0x00000004
    {
        //uprintf("\r\nWake by HIB INT LOW BAT");     // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
    // Reset button
    else
    {
        //uprintf("\r\nWake by Reset Button");      // THOM DEBUG (comment this line out)
        sys_samp.state = IDLE;
    }
}

/*
 * sleep_handler()
 * Determines what woke the module (wake pin, RTC match, cold boot).
 * Drops through to Main Menu if woke by keystroke and status is IDLE.
 * Sets status as DEPLOYED if woke by RTC match (status should already be DEPLOYED).
 * Sets status to COMMAND if woke by keystroke and status is DEPLOYED.
 * Anything else is assumed to be a power-on-reset.
 * RCG 4/14
 * TM 16 Apr 2018 - MFET_BOARD sleep issues
 *
 */
uint32_t sleep_handler(void)
{
	uint32_t uiStatus;

	uint32_t wakeSource = WAKE_NOTSET;

	int dbgFlg = 0;

	// Need to enable the Hibernation peripheral after wake/reset, before using it.
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);

	// EnableExpClk should always be called, even if the module was already enabled,
	// because this function also initializes some timing parameters.
	ROM_HibernateEnableExpClk(ROM_SysCtlClockGet());

	// Check if we just came out of hibernate mode
	if(ROM_HibernateIsActive())
	{
		uiStatus = ROM_HibernateIntStatus(false);	// Read the status to determine cause of wake.
		ROM_HibernateIntClear(uiStatus);			// Clear the hibernate interrupt

		// Wake pin
		if(uiStatus & HIBERNATE_INT_PIN_WAKE)           // 0x00000008
		{
		    if(dbgFlg) uprintf("\r\nWake by pin\r\n");              // serial console activity circuit, or WAKE switch
		    wakeSource = WAKE_CONSOLE;
		}

		// RTC match
		else if(uiStatus & HIBERNATE_INT_RTC_MATCH_0)	// 0x00000001  Take a sample
		{
		    if(dbgFlg) uprintf("\r\nWake by RTC match\r\n");
		    wakeSource = WAKE_RTC;
		}
		else if(uiStatus & HIBERNATE_INT_VDDFAIL)       // 0x00000080
		{
		    if(dbgFlg) uprintf("\r\nWake by HIB INT VDDFAIL\r\n");
            sys_samp.state = IDLE;      // Drop through to main menu
		}

		else if(uiStatus & HIBERNATE_INT_RESET_WAKE)        // 0x00000040
		{
		    if(dbgFlg) uprintf("\r\nWake by HIB INT RESET WAKE\r\n");
		    //wakeSource = WAKE_RESET;      // THOM NEW 2 Mar 2022
            sys_samp.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_GPIO_WAKE)         // 0x00000020
		{
		    if(dbgFlg) uprintf("\r\nWake by HIB INT GPIO WAKE\r\n");
		    //wakeSource = WAKE_GPIO;
            sys_samp.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_WR_COMPLETE)       // 0x00000010
		{
		    // This event is what happens when power is cycled during sampling (RTC wakeup)

		    //Wake by HIB INT WR COMPLETE
		    //
		    //(HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_RTC_MATCH_0) in sleep.c
		    //
		    //Power On Reset

		    if(dbgFlg) uprintf("\r\nWake by HIB INT WR COMPLETE\r\n");      // this signal is active when RESET switch is pressed...
            //sys_samp.state = IDLE;      // Drop through to main menu  // commented out 21 Jul 2021
	        // 21 Jul 2021 Working to fix a bug for nsFOCE, if asleep, power cycle no problem.  if awake, power cycle causes main menu (drops out of deploy)
		    //wakeSource = WAKE_RESET;

		    // NOTE:
		    // if power is cycled when in DEPLOY and RTC sampling, the desired behavior is to resume sampling....
		    if(sys_samp.state == DEPLOYED)
		    {
		        // THOM DEBUG
		        if(dbgFlg) uprintf("Pretending that RTC wake event happened even tho it was a power cycle - keep on sampling...\r\n");
		        wakeSource = WAKE_RTC;
		    }
		}
		else if(uiStatus & HIBERNATE_INT_PIN_WAKE)          // 0x00000008
		{
		    if(dbgFlg) uprintf("\r\nWake by HIB INT PIN WAKE\r\n");
            sys_samp.state = IDLE;      // Drop through to main menu
		}
		else if(uiStatus & HIBERNATE_INT_LOW_BAT)           // 0x00000004
		{
		    if(dbgFlg) uprintf("\r\nWake by HIB INT LOW BAT\r\n");
            sys_samp.state = IDLE;      // Drop through to main menu
		}
		// Reset button
		else
		{
		    if(dbgFlg) uprintf("\r\nWake by Reset Button\r\n");
            sys_samp.state = IDLE;      // Drop through to main menu
		}

		// THOM - need to check VDD3ON usage from MFET (TODO)
		HibernateGPIORetentionDisable();	// Return control of GPIO pins to CPU (clears VDD3ON bit)
	}

	// THOM NOTE: this code does not execute at power on wakeup.   TODO to figure out if HIBERNATE_INT_WR_COMPLETE

	// Otherwise, power on reset, finish configuring hibernation module
	if(!(uiStatus & (HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_RTC_MATCH_0)))
	{
	    //uprintf("\r\nHey Man, 200msec delay in Sleep.c in sleep_handler\r\n\r\n");

	    if(dbgFlg)
	    {
            // {IDLE, START, DEPLOYED, COMMAND}
            if(sys_samp.state == IDLE) uprintf("sys_samp.state = IDLE\r\n");
            if(sys_samp.state == START) uprintf("sys_samp.state = START\r\n");
            if(sys_samp.state == DEPLOYED) uprintf("sys_samp.state = DEPLOYED\r\n");
            if(sys_samp.state == COMMAND) uprintf("sys_samp.state = COMMAND\r\n");
	    }


	    if(dbgFlg) uprintf("\r\n(HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_RTC_MATCH_0) in sleep.c\r\n");
		HibernateClockConfig(HIBERNATE_OSC_LOWDRIVE);

		// Next two lines prevent hibernate lockup (http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/275336.aspx)
		ROM_HibernateRTCTrimSet (0x7FFF);
		HibernateIntDisable (HIBERNATE_INT_PIN_WAKE | HIBERNATE_INT_LOW_BAT | HIBERNATE_INT_RTC_MATCH_0 | HIBERNATE_INT_WR_COMPLETE);

		// Let 32 kHz crystal stabilize
		ROM_SysCtlDelay(MILLISECOND*200);
		ROM_HibernateRTCEnable();	// Enable Real Time Clock

		// 21 Jul 2021 Working to fix a bug for nsFOCE, if asleep, power cycle no problem.  if awake, power cycle causes main menu (drops out of deploy).   This code is not executed in that case.
		//sys_samp.state = IDLE;      // Drop through to main menu  // commented out 21 Jul 2021

		// 14 mar 2022 - Power Event while RTC sampling, re-program the wakeup time and go to sleep


		if(dbgFlg) uprintf("\r\nPower On Reset\r\n");
	}

	return(wakeSource);

}

