/****************************************************************************
 Module
   GameControlService.c

 Revision
   1.0.1

 Description
   This is the first service for the Test Harness under the
   Gen2 Events and Services Framework.

 Notes

 History
 When           Who     What/Why
 -------------- ---     --------
 10/26/17 18:26 jec     moves definition of ALL_BITS to ES_Port.h
 10/19/17 21:28 jec     meaningless change to test updating
 10/19/17 18:42 jec     removed referennces to driverlib and programmed the
                        ports directly
 08/21/17 21:44 jec     modified LED blink routine to only modify bit 3 so that
                        I can test the new new framework debugging lines on PF1-2
 08/16/17 14:13 jec     corrected ONE_SEC constant to match Tiva tick rate
 11/02/13 17:21 jec     added exercise of the event deferral/recall module
 08/05/13 20:33 jec     converted to test harness service
 01/16/12 09:58 jec     began conversion from TemplateFSM.c
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
// This module
#include "GameControlService.h"

// Hardware
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"
#include "driverlib/pwm.h"
#include "inc/hw_pwm.h"

// Event & Services Framework
#include "ES_Configure.h"
#include "ES_Framework.h"
#include "ES_DeferRecall.h"
#include "ES_ShortTimer.h"
#include "ES_Port.h"

//Include other files
#include "CompassService.h"
#include "GameplayHSM.h"
#include "HardwareInitialize.h"
#include "CollisionModule.h"
#include "BeaconSense.h"
#include "ServoModule.h"
#include "MotorSM.h"
#include "PWM_Module.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)

//Time to dump
#define DUMPTIME 100

//Hardware bits
#define TEAMSWITCH

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this service.They should be functions
   relevant to the behavior of this service
*/
void SaveColorFreq(uint8_t SPI);
static void TurnOnPWMEmitters(void);

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

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

static uint16_t GameTime = 0;

// Compass query variables
static uint8_t  AssignedTeam;
static uint8_t  ColorAssigned;
static uint16_t FreqAssigned;
static uint8_t  EastRecycleColor;
static uint8_t  WestRecycleColor;

// Adding history for recycling center
static uint8_t  LastEastRecycleColor;
static uint8_t  LastWestRecycleColor;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitGameControlService

 Parameters
     uint8_t : the priority of this service

 Returns
     bool, false if error in initialization, true otherwise

 Description
     Calls initialization for all non-framework modules in 218B project:
      - HardwareInitialize
      - BeaconSense
      - CollisionModule
      - Servos

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

 Notes

 Author
     J. Edward Carryer, 01/16/12, 10:00
****************************************************************************/
bool InitGameControlService(uint8_t Priority)
{
  ES_Event_t ThisEvent;
  MyPriority = Priority;

  // initialize all hardware
  InitializeHardware();

  // initialize collision module
  InitializeCollisionDetection();

  // initialize beacon sensors
  InitializeBeaconSense();

  // set up servos
  SetupAllServoFrequencies();
  SetServoStartingPositions();

  // turn on recycling emitter
  TurnOnPWMEmitters();

  // post the initial transition event
  ThisEvent.EventType = ES_INIT;
  CurrentState = InitGState;

  if (ES_PostToService(MyPriority, ThisEvent) == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

/****************************************************************************
 Function
     PostGameControlService

 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
     J. Edward Carryer, 10/23/11, 19:25
****************************************************************************/
bool PostGameControlService(ES_Event_t ThisEvent)
{
  return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunGameControlService

 Parameters
   ES_Event_t : the event to process

 Returns
   ES_Event_t, ES_NO_EVENT if no error ES_ERROR otherwise

 Description

 Notes

 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event_t RunGameControlService(ES_Event_t ThisEvent)
{
  ES_Event_t      ReturnEvent;
  ES_Event_t      NextEvent;
  static uint16_t TeamByte = 0;
  static uint8_t  RemainingTime;
  ReturnEvent.EventType = ES_NO_EVENT; // assume no errors
  //Pass NextState to CurrentState
  switch (CurrentState)
  {
    case InitGState:
    {
      if (ThisEvent.EventType == ES_INIT)
      {
        //Read team designation switch
        TeamByte = (HWREG(GPIO_PORTE_BASE + (GPIO_O_DATA + ALL_BITS)) >> 5);
        //Post to Compass Service the byte
        if (TeamByte == 0)
        {
          //printf("Team North! \n\r");
          //Register as Team North
          NextEvent.EventType = TEAM_SELECT;
          NextEvent.EventParam = 0x10;
          //Set team servo to correct position
          RaiseUSA();
        }
        else if (TeamByte == 1)
        {
          //printf("Team South! Viva Mexico! \n\r");
          //Register as Team South
          NextEvent.EventType = TEAM_SELECT;
          NextEvent.EventParam = 0x01;
          //Set team servo to correct position
          RaiseMexcio();
        }
        //Post event to Compass Service
        PostCompassService(NextEvent);
        //Transition for next State
        CurrentState = WaitingforREG;
      }
    }
    break;
    case WaitingforREG:
    {
      //Waiting for Team registration success from Compass service
      if (ThisEvent.EventType == REG_COMPLETE)
      {
        //printf("Registration complete!\n\r");
        //Call getter functions from Compass Service
        AssignedTeam = GetTeamAssignment();
        ColorAssigned = GetColorAssignment();
        FreqAssigned = GetFreqAssignment();
        EastRecycleColor = GetEastRecycleColor();
        WestRecycleColor = GetWestRecycleColor();
        //Start 100ms timer
        ES_Timer_InitTimer(GameControlTimer, 100);
        //Turn off Game indicator LED
        HWREG(GPIO_PORTF_BASE + (GPIO_O_DATA + ALL_BITS)) |= BIT1HI;
        //Set transition for GameOff State
        CurrentState = GameOff;
      }
    }
    break;
    case GameOff:
    {
      //Waiting for events
      switch (ThisEvent.EventType)
      {
        case ES_TIMEOUT:
        {
          //printf("Game inactive...\n\r");
          //Call getter functions from Compass Service
          AssignedTeam = GetTeamAssignment();
          ColorAssigned = GetColorAssignment();
          FreqAssigned = GetFreqAssignment();
          EastRecycleColor = GetEastRecycleColor();
          WestRecycleColor = GetWestRecycleColor();

          //Start 1s timer
          ES_Timer_InitTimer(GameControlTimer, 1000);
          //Set transition to next state
          CurrentState = GameOff;
        }
        break;

        case GAME_STARTED:
        {
          //Turn on game indicator LED
          HWREG(GPIO_PORTF_BASE + (GPIO_O_DATA + ALL_BITS)) &= BIT1LO;
          //Set transition to next state
          CurrentState = GameOn;
        }
      }
    }
    break;

    case GameOn:
    {
      if (ThisEvent.EventType == ES_TIMEOUT)
      {
        //printf("\r\nGame Active");
        //Call getter functions from Compass Service
        AssignedTeam = GetTeamAssignment();
        ColorAssigned = GetColorAssignment();
        FreqAssigned = GetFreqAssignment();
        EastRecycleColor = GetEastRecycleColor();
        WestRecycleColor = GetWestRecycleColor();
        //Check if frequency has changed or not
        if ((WestRecycleColor != LastWestRecycleColor) || (EastRecycleColor != LastEastRecycleColor))
        {
          //POST CHANGE FREQUENCY EVENT HERE
          NextEvent.EventType = RECYCLING_CHANGE;
          PostGameplayMasterSM(NextEvent);
        }
        //set GameTime += 1
        GameTime++;
        //printf("\r\nGameTime: %d\r\n", GameTime);
        RemainingTime = 138 - GameTime;
        //if(138 - GameTime) < TimeToDump: post START_DUMPING
        if (RemainingTime < DUMPTIME)
        {
          NextEvent.EventType = START_DUMPING;
          PostGameControlService(NextEvent);
          PostGameplayMasterSM(NextEvent);
        }
        //Start timer for 1000ms
        ES_Timer_InitTimer(GameControlTimer, 1000);
        //Set transition to next state
        CurrentState = GameOn;
      }
      else if (ThisEvent.EventType == START_DUMPING)
      {
        //Set transition to next state

        CurrentState = TimeToDump;
      }
      else if (ThisEvent.EventType == GAME_OVER)
      {
        //printf("Game Over %d", GameTime);
        //Set GameTime = 0
        GameTime = 0;
        //post MOTOR_COMMAND (EventParam = STOP)
        NextEvent.EventType = MOTOR_COMMAND;
        NextEvent.EventParam = STOP;
        PostMotorSM(NextEvent);
        //Turn off game indicator LED
        HWREG(GPIO_PORTF_BASE + (GPIO_O_DATA + ALL_BITS)) |= BIT1HI;
        //Post transition for next event
        CurrentState = GameOff;
      }
      //Save history of last state
      LastEastRecycleColor = EastRecycleColor;
      LastWestRecycleColor = WestRecycleColor;
    }
    break;

    case TimeToDump:
    {
      switch (ThisEvent.EventType)
      {
        case ES_TIMEOUT:
        {
          //printf("Clutch time, Time: %d\n\r",GameTime);
          GameTime++;

          if (GameTime == 0)
          {
            // it is game over
            NextEvent.EventType = GAME_OVER;
            PostGameControlService(NextEvent);
            PostGameplayMasterSM(NextEvent);
            PostCompassService(NextEvent);
          }

          //Call getter functions from Compass Service
          AssignedTeam = GetTeamAssignment();
          ColorAssigned = GetColorAssignment();
          FreqAssigned = GetFreqAssignment();
          EastRecycleColor = GetEastRecycleColor();
          WestRecycleColor = GetWestRecycleColor();
          if ((WestRecycleColor != LastWestRecycleColor) || (EastRecycleColor != LastEastRecycleColor))
          {
            //POST CHANGE FREQUENCY EVENT HERE
            NextEvent.EventType = RECYCLING_CHANGE;
            PostGameplayMasterSM(NextEvent);
          }

          //Start timer for 1000ms
          ES_Timer_InitTimer(GameControlTimer, 1000);
          //Set transition to next state
          CurrentState = TimeToDump;
        }
        break;

        case GAME_OVER:
        {
          printf("Game Over %d", GameTime);
          //Set GameTime = 0
          GameTime = 0;
          //post MOTOR_COMMAND (EventParam = STOP)
          NextEvent.EventType = MOTOR_COMMAND;
          NextEvent.EventParam = STOP;
          PostMotorSM(NextEvent);
          //Turn off game indicator LED
          HWREG(GPIO_PORTF_BASE + (GPIO_O_DATA + ALL_BITS)) |= BIT1HI;
          //Post transition for next event
          CurrentState = GameOff;
        }
        break;
      }
      LastEastRecycleColor = EastRecycleColor;
      LastWestRecycleColor = WestRecycleColor;
    }
    break;
  }
  return ReturnEvent;
}

/****************************************************************************
 Function
     QueryGameControl

 Parameters
     None

 Returns
     GameControlState_t The current state of the state machine

 Description
     returns the current state of the GameControl state machine

 Notes

 Author
     Bibit Bianchini, 2/21/2019
****************************************************************************/
GameControlState_t QueryGameControl(void)
{
  return CurrentState;
}

/****************************************************************************
 Collection of Functions
    GetOurLandfillFreq
    GetOurRecycleBeaconFreq

 Parameters
   None

 Returns
   uint16_t : the frequencies

 Description
   Returns the frequencies of our current landfill and recyling center
   beacons.

 Notes

 Author
   Bibit Bianchini, 2/25/2019
****************************************************************************/
uint16_t GetOurLandfillFreq(void)
{
  // based on what team we are on
  if (GetTeamAssignment() == NORTH)
  {
    return NorthLandfillFreq;
  }
  else
  {
    return SouthLandfillFreq;
  }
}

uint16_t GetOurRecycleBeaconFreq(void)
{
  // based on which recycling center is accepting our color
  uint8_t OurColor = GetColorAssignment();
  if (GetEastRecycleColor() == OurColor)
  {
    return EastRecyclingFreq;
  }
  else
  {
    return WestRecyclingFreq;
  }
}

/***************************************************************************
 private functions
 ***************************************************************************/
static void TurnOnPWMEmitters(void)
{
  uint16_t RecycleOpenFreq;
  RecycleOpenFreq = GetOurRecycleBeaconFreq();
  //Set Initial PWM Frequency
  PWM_SetFrequency(RecycleOpenFreq, RecycleEmitter);
  //Turn off PWM frequency
  PWM_SetDuty(50, RecycleEmitter);
  //Enable PWM line
  HWREG(PWM1_BASE + PWM_O_ENABLE) |= PWM_ENABLE_PWM0EN;
}

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