/****************************************************************************
 Module
  AirLeak_SM.c

 Revision
   1.

 Description
   State machine for Air Leak interaction

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 11/08/19 19:00 gab     pseudo-code
 11/18/19 16:42 ram		random exclusive function added 
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* include header files for this service
*/

#include "AirLeak_SM.h"
#include "AirLeakLib.h"
#include "ResetService.h"
#include "ArcadeFSM.h"

#include <stdio.h>
#include <stdlib.h>
#include "Meteor_SM.h"
#include <string.h>
#include "Meteor.h"
#include "Cannon.h"
#include "Cannon_SM.h"
#include "ArcadeFSM.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"



/*----------------------------- Module Defines ----------------------------*/
#define ONE_SEC 1000 // this time assume a 1.000mS/tick timing
#define MAX_TIME (ONE_SEC*10) //Define maximum time to plugg the leak (10 seconds)
#define STEP_TIME (ONE_SEC/2) //Define Time between steps
#define N_STEPS (MAX_TIME/STEP_TIME) //Define total number of steps
#define MAX_OXIGEN_LEVEL 100 //Define maximum oxigen level
#define STEP_OXIGEN MAX_OXIGEN_LEVEL/N_STEPS //Define step size of oxigen gauge



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

static void SaveHallData(void) ;
static uint8_t RandomExclusive(uint16_t SysTime);


/*---------------------------- Module Variables ---------------------------*/
// with the introduction of Gen2, we need a module level Priority variable
static uint8_t MyPriority;
static AirLeakState_t CurrentState;

static uint8_t Fan=3; //Active Fan (0,1,2, initiate to an invalid number)
static uint8_t HallData[3];//Hall sensors data
static uint8_t previous_HallData[3];//Hall sensors data
static uint8_t MachineCounter; //Tracks # times service called during a game
static uint8_t Indices[] = {0, 1, 2}; // List of fans


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitializeAirLeak

 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
    Gabriela Bravo, 11/08/19, 19:04
****************************************************************************/
bool InitAirLeak_SM( uint8_t Priority )
{
  //save initial state of hall sensors
  AirLeak_ReadHallSensors(previous_HallData);

  //Initialize the MyPriority variable with the passed in parameter.
  MyPriority = Priority;

  //Set CurrentState to be Idle_A
  CurrentState=Idle_A;

  return true;
}

/****************************************************************************
 Function
     PostAirLeakSM

 Parameters
     ES_Event_t 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
     Gabriela Bravo, 11/08/19, 19:27
****************************************************************************/
bool PostAirLeak_SM( ES_Event_t ThisEvent )
{
  return ES_PostToService( MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunAirLeakSM

 Parameters
   ES_Event_t : the event to process. This event will be one of:
   Tot_Inserted, Leak_Develops, End_Of_Game, RST, ES_TIMEOUT, AirflowPlugged


 Returns
   ES_NO_EVENT

 Description
Control game Air Leaks.


 Notes

 Author
   Gabriela Bravo, 11/08/19, 19:20
****************************************************************************/
ES_Event_t RunAirLeak_SM( ES_Event_t ThisEvent )
{
  ES_Event_t ReturnEvent;
  ES_Event_t NewEvent;
  ReturnEvent.EventType=ES_NO_EVENT;

switch (CurrentState)
{
  case Idle_A ://CurrentState is Idle_A
  {//if tot inserted
    if (ThisEvent.EventType == Tot_Inserted){
      //set oxygen gauge to max
      AirLeak_MoveGauge(MAX_OXIGEN_LEVEL);
      //Set indices[] equal {0, 1, 2} to reset list
      uint8_t index;
      for(index=0; index <3; index++)
      {
        Indices[index] = index;
      }
    }//end if

    //if Leak_Develops
     if(ThisEvent.EventType == Leak_Develops)
      {

        uint16_t SysTime;
        //Use system time to randomly select the fan
        SysTime = ES_Timer_GetTime();
        Fan = RandomExclusive(SysTime);
        // Turn the fan on
        AirLeak_FanControl(Fan, On);
        //Start timer
        ES_Timer_InitTimer(AIRLEAK_TIMER, STEP_TIME);
		CurrentState = FanOn;
		//Increment MachineCounter
		MachineCounter++;
      }
      else if(ThisEvent.EventType == End_Of_Game || ThisEvent.EventType == RST)
      {
        MachineCounter = 0;
      }
     }

  break;
    //End Idle_A state
  case FanOn ://CurrentState is On
  {
    uint8_t Oxigen_Level = AirLeak_QueryGauge();
    //If TIME_OUT and Oxygen_Level greater than 0
    if (ThisEvent.EventType ==ES_TIMEOUT && Oxigen_Level>0){
      //Decrement oxygen gauge
      AirLeak_MoveGauge(Oxigen_Level - STEP_OXIGEN);
      //init timer
      ES_Timer_InitTimer(AIRLEAK_TIMER, STEP_TIME);
    }//end if

    //If TIME_OUT Oxygen_Level less or equal to 0
    else if (ThisEvent.EventType ==ES_TIMEOUT && Oxigen_Level<=0){
      //Post to all End_Of_Game with parameter Game_Over
      NewEvent.EventType=End_Of_Game;
      NewEvent.EventParam = GAME_OVER;
      ES_PostAll(NewEvent);
      //Turn off fan
      AirLeak_FanControl(Fan,Off);
      //move to Idle_A state
      CurrentState = Idle_A;
      //set Fan to invalid
      Fan=3;
    }//end if


    //If AirflowPlugged
    if(ThisEvent.EventType ==Airflow_Plugged){
      //Turn off Fan
      AirLeak_FanControl(Fan,Off);
      //Post Interaction_Completed to Arcade_SM
      NewEvent.EventType = Interaction_Completed;
      PostArcadeFSM(NewEvent);

      //Set oxygen gauge to maximum
      AirLeak_MoveGauge(MAX_OXIGEN_LEVEL);
      //move to Idle_A state
      CurrentState = Idle_A;
      //set Fan to invalid
      Fan=3;
    }//end if

    //if RST
    else if(ThisEvent.EventType == RST){
      //Turn off fan
      AirLeak_FanControl(Fan,Off);
      //move to Idle_A state
      CurrentState = Idle_A;
      //FanIndex = 0;
      MachineCounter = 0;
      //set Fan to invalid
      Fan=3;
    }//end if

    //If End_Of_Game
    else if(ThisEvent.EventType == End_Of_Game){
      //Turn off fan
      AirLeak_FanControl(Fan,Off);
      //move to Idle_A state
      CurrentState = Idle_A;
      //FanIndex = 0;
      MachineCounter = 0;
      //set Fan to invalid
      Fan=3;
    }//end if
}
    break;



}
//Return ES_NO_EVENT
return ReturnEvent;

}

/***************************************************************************
 private functions
 ***************************************************************************/
/****************************************************************************
 Function
    AirPlugChecker

 Parameters
   None

 Returns
   bool. True is a new event has been published

 Description
 Check if there is an Air leak active, if the plug is detected by the hall sensor.
 It also check is any of the plug is inserted or taken off, this count as a user
 interaction for the ResetService

 Notes

 Author
Gabriela Bravo, 11//19, 16:00
****************************************************************************/
 bool AirPlugChecker(void) {
   ES_Event_t ReturnEvent;
   bool ReturnVal = false;

   //read hall sensors data
   AirLeak_ReadHallSensors(HallData);

	 //if active leak plugged, post to Airleak_SM
   if(Fan<3){
     if(HallData[Fan]==0 )
	 {
       //post that air leak was plugged
       ReturnEvent.EventType=Airflow_Plugged;
       PostAirLeak_SM(ReturnEvent);
       //save hall data as previous state
       SaveHallData();
       //return true
       ReturnVal = true;
     }
 }
   //if any of the sensors changed state, count it as a new interaction
   if ((HallData[0]!=previous_HallData[0]) || (HallData[1]!=previous_HallData[1]) ||
     (HallData[2]!=previous_HallData[2]) ) 
	 {
      //post to reset service
     ReturnEvent.EventType=Airflow_Plugged;
     PostResetService(ReturnEvent);
     //return true
     SaveHallData();
     ReturnVal = true;
   }

   //return
     return ReturnVal;
 }

/****************************************************************************
 Function
    SaveHallData

 Parameters
   None

 Returns
   None

 Description
Function to copy from the array HallData to the array previous_HallData
 Notes

 Author
 Gabriela Bravo, 11//19, 19:00
****************************************************************************/
static void SaveHallData(void) {
  //copy to array
	int index = 0;
	for(index=0;index<3;index++)
	{
		//copy by element
		previous_HallData[index] = HallData[index];
 }
}

/***
RandomExclusive Function Description
	Arguments: System Clock Time
	Returns: Fan Number
	This function picks a random fan from the list, return that value, and
	then "removes" that fan from the list so it cannot be picked again.

	Author: R. Merchant
***/
static uint8_t RandomExclusive(uint16_t SysTime)
{
    uint8_t ReturnVal;
    //create static array to store indices
    int8_t counter = 3 - MachineCounter;
    //Create int to track position in the array
    uint8_t index;
    //If count is valid
    if(counter > 0)
    {
        // Get mod of the division by the counter
        index = SysTime%counter;
        // Get array element at mod result
       ReturnVal = Indices[index];
        //Start loop to remove the element and push
        //everything above it down.
        uint8_t lpcnt;
        for (lpcnt = index ; lpcnt < 3; lpcnt++)
        {
            //Over write n element with n+1
            Indices[lpcnt] = Indices[lpcnt+1];
        }
    } // End if
    return(ReturnVal);
}
/*------------------------------- Footnotes -------------------------------*/
/*------------------------------ End of file ------------------------------*/