/****************************************************************************
 Module
   ArcadeFSM.c

 Revision
   1.0.1

 Description
   This is the ArcadeFSM
   Gen2 Events and Services Framework.

 Notes

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this service
*/
#include "ArcadeFSM.h"
#include <string.h>
#include "Crisis.h"
#include "PowerCrank_SM.h"
#include "Tot.h"
#include "AirLeakLib.h"
#include "Meteor_SM.h"
#include "AirLeak_SM.h"
#include "Servo.h"
#include "Meteor.h"


/* include header files for hardware access
*/
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"

/* include header files for the framework
*/
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_DeferRecall.h"
#include "ES_ShortTimer.h"
#include "termio.h"




/*----------------------------- Module Defines ----------------------------*/
// these times assume a 1.000mS/tick timing
#define ONE_SEC 1000
#define HALF_SEC (ONE_SEC/2)
#define TWO_SEC (ONE_SEC*2)
#define FIVE_SEC (ONE_SEC*5)
#define NUM_INTERACTIONS 8 //Not counting initial power regen interaction
#define WIN 1
#define GAME_OVER 0
#define DELAY_TIME 500

#define TOT_TIME 2000 //changed from 2000

#define AIRLEAK_INTERACTION 2
#define METEOR_INTERACTION 3

#define   TIME_BTW_INTERACTIONS (2.5*ONE_SEC)
#define BUZZER_TIME (ONE_SEC)

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this service.They should be functions
   relevant to the behavior of this service
*/

bool TotChecker(void);



/*---------------------------- Module Variables ---------------------------*/
// with the introduction of Gen2, we need a module level Priority variable
static uint8_t MyPriority;
static ArcadeFSMState_t CurrentSMState;
UI_State_t CrisisLEDState;


static const uint8_t InteractionList[NUM_INTERACTIONS] = {METEOR_INTERACTION,
  METEOR_INTERACTION,AIRLEAK_INTERACTION,METEOR_INTERACTION,METEOR_INTERACTION,
  AIRLEAK_INTERACTION,METEOR_INTERACTION,METEOR_INTERACTION};

//Track index of InteractionList
static uint8_t InteractionIndex = 0;
UI_State_t BuzzerState;
static uint8_t LastTotState = 1;


// add a deferral queue for up to 3 pending deferrals +1 to allow for overhead
static ES_Event_t DeferralQueue[3+1];

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitArcadeFSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     bool, false if error in initialization, true otherwise

 Description
     Saves away the priority, and does any
     other required initialization for this service
 Notes

 Author
     Trey Weber
****************************************************************************/
bool InitArcadeFSM ( uint8_t Priority )
{
  MyPriority = Priority;

  //Initialize TOT
  TOT_HWInit();
  TOT_Block();

  Meteor_LightLEDBank(0, 0);

  //Intialzie Deferral Queue
  ES_InitDeferralQueueWith(DeferralQueue, ARRAY_SIZE(DeferralQueue));

  //Initialize AirLeak HW (includes crisis LEDs)
  AirLeak_Init();

  //Set SM currentstate
  CurrentSMState = NotPlaying;

  return true;
}




/****************************************************************************
 Function
     PostArcadeFSM

 Parameters
     EF_Event ThisEvent ,the event to post to the queue

 Returns
     bool false if the Enqueue operation failed, true otherwise

 Description
     Posts an event to this state machine's queue
 Notes

 Author
     Trey Weber
****************************************************************************/
bool PostArcadeFSM( ES_Event_t ThisEvent )
{
  return ES_PostToService( MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunArcadeFSM

 Parameters
   ES_Event_t : Tot_Inserted, End_Of_Game, RST, TIME_OUT, Interaction_Completed
   Global_Time_Out

 Returns
   ES_Event_t, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   Controls which is the current active game (power crank, air leak, or meteor)
   Controls the warning LED and BUZZER
   Controls the Release of TOT
   Control End_Of_Game due Global_Time_Out
 Notes

 Author
   Trey Weber
****************************************************************************/
ES_Event_t RunArcadeFSM( ES_Event_t ThisEvent )
{
  ES_Event_t NewEvent;
  ES_Event_t ReturnEvent;
  ReturnEvent.EventType = ES_NO_EVENT;

  switch (CurrentSMState) {
    case NotPlaying:
	//If TOT is inserted
	if (ThisEvent.EventType == Tot_Inserted) {
    Meteor_LightLEDBank(0, 0);
		//Start Low Power interaction
		NewEvent.EventType = Low_Power;
    PostPowerCrank_SM(NewEvent);
		//Change state to playing
		CurrentSMState = Playing;
		//Turn on Crisis LED
		CrisisLEDState = On;
		Crisis_Power(CrisisLEDState);
    ES_Timer_InitTimer(INTERACTION_TIMER, DELAY_TIME);
	}

    break;
    case Playing:
	//If we get a reset event or end of game
	if (ThisEvent.EventType == RST || ThisEvent.EventType == End_Of_Game) {
		//Reset state to not playing
		InteractionIndex = 0;
    ES_Timer_InitTimer(INTERACTION_TIMER, TOT_TIME);
    TOT_Release();
		CurrentSMState = WaitForTotRelease;
    BuzzerState = Off;
		Crisis_Buzzer(BuzzerState);
	}
	//Otherwise if we get a buzzer timeout
	else if (ThisEvent.EventType == ES_TIMEOUT) {
		//Turn off buzzer
		BuzzerState = Off;
		Crisis_Buzzer(BuzzerState);
		}
	//Otherwise if an interaction is complete
	else if (ThisEvent.EventType == Interaction_Completed) {
		//Turn off warning LEDs
		CrisisLEDState = Off;

		Crisis_Power(CrisisLEDState);
		Crisis_AirLeak(CrisisLEDState);
		Crisis_Meteor(CrisisLEDState);

		//Initialize timer (few seconds)
     ES_Timer_InitTimer(INTERACTION_TIMER, TIME_BTW_INTERACTIONS);

     CurrentSMState = WaitForNext;
	}
	//Otherwise if we get a global timeout
	else if (ThisEvent.EventType == Global_Time_Out) {
		//End of Game
		NewEvent.EventType = End_Of_Game;
    InteractionIndex = 0;

		//If power crank is Idle (query Power crank SM)
		PowerCrank_SMState_t CurrentPowerState = QueryPowerCrank_SM();
		if (CurrentPowerState == Idle_PC) {
			//Post win
			NewEvent.EventParam = WIN;
			ES_PostAll(NewEvent);
		}
		//Else....
		else {
			NewEvent.EventParam = GAME_OVER;
			ES_PostAll(NewEvent);
		}
	 ES_Timer_InitTimer(INTERACTION_TIMER, TOT_TIME);
    CurrentSMState = WaitForTotRelease;
	}
    break;
	case WaitForNext:
	//Check for Timeout
	if (ThisEvent.EventType == ES_TIMEOUT) {
     BuzzerState = Off;
		 Crisis_Buzzer(BuzzerState);
		//Check if we have reached end of event list
		if (InteractionIndex == NUM_INTERACTIONS) {
			//We have run out of events. User wins
			NewEvent.EventType = End_Of_Game;
			NewEvent.EventParam = WIN;
      ES_PostAll(NewEvent);
      InteractionIndex = 0;
      ES_Timer_InitTimer(INTERACTION_TIMER, TOT_TIME);
      CurrentSMState = WaitForTotRelease;
      TOT_Release();
		}
			//Check if next event in list is meteor
		else if (InteractionList[InteractionIndex] == METEOR_INTERACTION) {
			//Is meteor interaction
			//Post meteor interaction
			NewEvent.EventType = Meteor;
			PostMeteor_SM(NewEvent);
			//Increment Interaction Index
			InteractionIndex++;
			//Turn on warning LED
			CrisisLEDState = On;
			Crisis_Meteor(CrisisLEDState);

      CurrentSMState = Playing;
		}
		//Otherwise, check if Air leak event
		else if (InteractionList[InteractionIndex] == AIRLEAK_INTERACTION) {
			//Post Air leak event
			NewEvent.EventType = Leak_Develops;
			PostAirLeak_SM(NewEvent);
			//Increment InteractionIndex
			InteractionIndex++;
			//Turn on warning LED
			CrisisLEDState = On;
			Crisis_AirLeak(CrisisLEDState);
      CurrentSMState = Playing;

      //Init buzzer & timer
      BuzzerState = On;
      Crisis_Buzzer(BuzzerState);
			ES_Timer_InitTimer(BUZZER_TIMER, BUZZER_TIME);
		}
		//Return Error
		else  {
		//One of the above conditions was not satisfied
		ReturnEvent.EventType = ES_ERROR;
		}
	}
	//Else check for RST or End of Game
	else if (ThisEvent.EventType == RST || ThisEvent.EventType == End_Of_Game) {
	  	//Reset state to not playing
		InteractionIndex = 0;
    ES_Timer_InitTimer(INTERACTION_TIMER, TOT_TIME);
    BuzzerState = Off;
    //Release Tot
    TOT_Release();
		Crisis_Buzzer(BuzzerState);
		CurrentSMState = WaitForTotRelease;
	}
	//Else if we get a global timeout event
	else if (ThisEvent.EventType == Global_Time_Out) {
		//End of Game
		NewEvent.EventType = End_Of_Game;
    InteractionIndex = 0;
		//If power crank is Idle (query Power crank SM)
		PowerCrank_SMState_t CurrentPowerState = QueryPowerCrank_SM();
		if (CurrentPowerState == Idle_PC) {
			//Post win
			NewEvent.EventParam = WIN;
			ES_PostAll(NewEvent);
		}
		//Else....
		else {
			NewEvent.EventParam = GAME_OVER;
			ES_PostAll(NewEvent);
		}
    ES_Timer_InitTimer(INTERACTION_TIMER, TOT_TIME);
    CurrentSMState = WaitForTotRelease;
	}
  break;

    case WaitForTotRelease:
	//If TOT is inserted
	if (ThisEvent.EventType == ES_TIMEOUT)
    {
      //Block the TOT channel
      TOT_Block();
      //Set State to not playing
      CurrentSMState = NotPlaying;
      ES_RecallEvents(MyPriority, DeferralQueue);
	}
      else if (ThisEvent.EventType == Tot_Inserted)
    {
      ES_DeferEvent(DeferralQueue, ThisEvent);
    }
	break;
  }

  return ReturnEvent;
}
/***************************************************************************
 service event checking functions
 ***************************************************************************/
 /***
TotChecker Function Description
 	Arguments: none
 	Returns: bool. True if ther is a new event
 	This function check for the insertion of the tot

 	Author: Trey Weber
 ***/

bool TotChecker(void) {
  ES_Event_t NewEvent;
  bool ReturnVal = false;

  //Get current TOT state
  uint8_t CurrentTotState = TOT_Detect();
  //Report change only if we aren't playing
  if (CurrentSMState==NotPlaying){
    //Check if state has changed
    if (CurrentTotState != LastTotState) {
      //Check if high or low
      if (CurrentTotState != 1) {
        //Tot detected, let all these damn services know!
        NewEvent.EventType = Tot_Inserted;
        ES_PostAll(NewEvent);
          }
      //Else TOT has been ejected into outer space
      //Do Nothing!
      ReturnVal = true; //Return successfully
    }
}
  //Set new state
  LastTotState = CurrentTotState;

  //Return
  return ReturnVal;
}
/***************************************************************************
 private functions
 ***************************************************************************/

/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/