/****************************************************************************
 Module
   Gameplay_GameTasksSM.c

 Revision
   2.0.1

 Description
   This is a sub state machine (2nd level) called GameTasks within the
   Gameplay module for ME 218B project for Team 3.  Implements a hierarchical
   state machine under the Gen2 Events and Services Framework.

 Notes


 History
 When           Who     What/Why
 -------------- ---     --------
 02/21/19       bibit   converted from template to project file
 02/27/17 09:48 jec     another correction to re-assign both CurrentEvent
                        and ReturnEvent to the result of the During function
                        this eliminates the need for the prior fix and allows
                        the during function to-remap an event that will be
                        processed at a higher level.
 02/20/17 10:14 jec     correction to Run function to correctly assign
                        ReturnEvent in the situation where a lower level
                        machine consumed an event.
 02/03/16 12:38 jec     updated comments to reflect changes made in '14 & '15
                        converted unsigned char to bool where appropriate
                        spelling changes on true (was True) to match standard
                        removed local var used for debugger visibility in 'C32
                        commented out references to Start & RunLowerLevelSM so
                        that this can compile.
 02/07/13 21:00 jec     corrections to return variable (should have been
                        ReturnEvent, not CurrentEvent) and several EV_xxx
                        event names that were left over from the old version
 02/08/12 09:56 jec     revisions for the Events and Services Framework Gen2
 02/13/10 14:29 jec     revised Start and run to add new kind of entry function
                        to make implementing history entry cleaner
 02/13/10 12:29 jec     added NewEvent local variable to During function and
                        comments about using either it or Event as the return
 02/11/10 15:54 jec     more revised comments, removing last comment in during
                        function that belongs in the run function
 02/09/10 17:21 jec     updated comments about internal transitions on During funtion
 02/18/09 10:14 jec     removed redundant call to RunLowerlevelSM in EV_Entry
                        processing in During function
 02/20/07 21:37 jec     converted to use enumerated type for events & states
 02/13/05 19:38 jec     added support for self-transitions, reworked
                        to eliminate repeated transition code
 02/11/05 16:54 jec     converted to implment hierarchy explicitly
 02/25/03 10:32 jec     converted to take a passed event parameter
 02/18/99 10:19 jec     built template from MasterMachine.c
 02/14/99 10:34 jec     Began Coding
****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
// Basic includes for a program using the Events and Services Framework
#include "ES_Configure.h"
#include "ES_Framework.h"

#include "inc/hw_pwm.h"
#include "inc/hw_gpio.h"
#include "inc/hw_sysctl.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_nvic.h"
#include "inc/hw_ssi.h"
#include "inc/hw_Timer.h"

/* include header files for this state machine as well as any machines at the
   next lower level in the hierarchy that are sub-machines to this machine
*/
// this module's header file
#include "Gameplay_GameTasksSM.h"

// this module's lower level header files
#include "Gameplay_GameTasks_CollectBallsSM.h"
#include "Gameplay_GameTasks_DumpRecyclingSM.h"
#include "Gameplay_GameTasks_DumpTrashSM.h"

// include necessary connections for test harness
#include "GP_MapKeys.h"

// other modules
#include "BeaconSense.h"
#include "GameControlService.h"
#include "MotorSM.h"
#include "GameplayHSM.h"
#include "ServoModule.h"

/*----------------------------- Module Defines ----------------------------*/
// define constants for the states for this machine
// and any other local defines

#define ENTRY_STATE CollectBalls

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine, things like during
   functions, entry & exit functions. They should be functions relevant to
   the behavior of this state machine
*/
static ES_Event_t DuringCollectBalls(ES_Event_t Event);
static ES_Event_t DuringDumpRecycling(ES_Event_t Event);
static ES_Event_t DuringDumpTrash(ES_Event_t Event);

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well
static GameTasksState_t CurrentState;

static uint16_t         BeaconRearFreq;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
    RunGameTasksSM

 Parameters
    ES_Event_t: the event to process

 Returns
    ES_Event_t: an event to return

 Description
    run function for the GameTasks state machine

 Notes
    uses nested switch/case to implement the machine.

 Author
    Bibit Bianchini, 2/21/2019
****************************************************************************/
ES_Event_t RunGameTasksSM(ES_Event_t CurrentEvent)
{
  bool              MakeTransition = false; // making a state transition?
  GameTasksState_t  NextState = CurrentState;

  // default to normal entry to new state
  ES_Event_t  EntryEventKind = { ES_ENTRY, 0 };
  ES_Event_t  ReturnEvent = CurrentEvent; // assume we don't consume event

  switch (CurrentState)
  {
    case CollectBalls:       // If current state is CollectBalls
    {
      // Execute During function for CollectBalls. ES_ENTRY & ES_EXIT are
      // processed here to allow the lower level state machines to re-map or
      // consume the event
      ReturnEvent = CurrentEvent = DuringCollectBalls(CurrentEvent);

      // process any events
      if (CurrentEvent.EventType != ES_NO_EVENT)   // if an event is active
      {
        switch (CurrentEvent.EventType)
        {
          case RECYCLE_FULL: // if our robot has reached max recycle capacity
          {
            // we want to move to DumpRecycling state
            NextState = DumpRecycling;

            // mark that we are taking a transition (for internal
            // transitions, skip changing MakeTransition)
            MakeTransition = true;

            // we do not want to enter DumpRecycling with history, so we will
            // leave the default to ES_ENTRY (make no change)
            //EntryEventKind.EventType = ES_ENTRY_HISTORY;

            // consume this event for the upper state machines
            ReturnEvent.EventType = ES_NO_EVENT;
          }
          break;

          case TRASH_FULL: // if our robot has reached max trash capacity
          {
            // we want to move to DumpTrash state
            NextState = DumpTrash;

            // mark that we are taking a transition (for internal
            // transitions, skip changing MakeTransition)
            MakeTransition = true;

            // we do not want to enter DumpTrash with history, so we will
            // leave the default to ES_ENTRY (make no change)
            //EntryEventKind.EventType = ES_ENTRY_HISTORY;

            // consume this event for the upper state machines
            ReturnEvent.EventType = ES_NO_EVENT;
          }
          break;

          case START_DUMPING: // if there is only enough time left to dump
          {
            // if we have any recycling balls, then we want to do that first.
            // we will have to query the BallSortingService:
            if (HowManyRecyclingBalls() > 0)
            {
              // we want to move to DumpRecycling state
              NextState = DumpRecycling;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter DumpRecycling with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
            // otherwise, if we have trash balls, then we will want to dump
            // trash.  we will have to query the BallSortingService:
            else if (HowManyTrashBalls() > 0)
            {
              // we want to move to DumpTrash state
              NextState = DumpTrash;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter DumpTrash with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }

            // otherwise, even if there is little time left, we don't have to
            // dump because our robot is empty; we will stay in CollectBalls
          }
          break;
        }
      }
    }
    break;  // end switch on CurrentState case for CollectBalls

    case DumpRecycling:       // If current state is DumpRecycling
    {
      // Execute During function for DumpRecycling. ES_ENTRY & ES_EXIT are
      // processed here to allow the lower level state machines to re-map or
      // consume the event
      ReturnEvent = CurrentEvent = DuringDumpRecycling(CurrentEvent);

      // process any events
      if (CurrentEvent.EventType != ES_NO_EVENT)   // if an event is active
      {
        switch (CurrentEvent.EventType)
        {
          case RECYCLING_CHANGE: // if the recycling center has changed
          {
            // we want to exit and re-enter our DumpRecycling state
            NextState = DumpRecycling;

            // mark that we are taking a transition (for internal
            // transitions, skip changing MakeTransition)
            MakeTransition = true;

            // we do not want to enter DumpRecycling with history, so we will
            // leave the default to ES_ENTRY (make no change)
            //EntryEventKind.EventType = ES_ENTRY_HISTORY;

            // consume this event for the upper state machines
            ReturnEvent.EventType = ES_NO_EVENT;
          }
          break;

          case RECYCLE_EMPTY: // if we emptied our recycling
          {
            // if we still have no trash balls, then we don't want to go to
            // DumpTrash and would instead CollectBalls.  Ask
            // BallSortingService:
            if (HowManyTrashBalls() == 0)
            {
              // we want to move to CollectBalls state
              NextState = CollectBalls;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter CollectBalls with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
            // if we still have a good bit of time left in the game, then we
            // want to go back to collecting balls, regardless of if we have
            // trash balls or not.  Ask GameControl service:
            else if (QueryGameControl() == GameOn)
            {
              // we want to move to CollectBalls state
              NextState = CollectBalls;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter CollectBalls with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
            // the last case is that we have little time left in the game and
            // we have trash balls to dump.  In this case, we want to go to
            // DumpTrash.
            else
            {
              // we want to move to DumpTrash state
              NextState = DumpTrash;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter DumpTrash with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
          }
          break;
        }
      }
    }
    break;  // end switch on CurrentState case for DumpRecycling

    case DumpTrash:       // If current state is DumpTrash
    {
      // Execute During function for DumpTrash. ES_ENTRY & ES_EXIT are
      // processed here to allow the lower level state machines to re-map or
      // consume the event
      ReturnEvent = CurrentEvent = DuringDumpTrash(CurrentEvent);

      // process any events
      if (CurrentEvent.EventType != ES_NO_EVENT)   // if an event is active
      {
        switch (CurrentEvent.EventType)
        {
          case TRASH_EMPTY: // if we emptied our trash
          {
            // if we still have no recycling balls, then we don't want to go
            // to DumpRecycling and would instead CollectBalls.  Ask
            // BallSortingService:
            if (HowManyRecyclingBalls() == 0)
            {
              // we want to move to CollectBalls state
              NextState = CollectBalls;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter CollectBalls with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }

            // if we still have a good bit of time left in the game, then we
            // want to go back to collecting balls, regardless of if we have
            // recycling balls or not.  Ask GameControl service:
            else if (QueryGameControl() == GameOn)
            {
              // we want to move to CollectBalls state
              NextState = CollectBalls;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter CollectBalls with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
            
            // the last case is that we have little time left in the game and
            // we have recycling balls to dump.  In this case, we want to go
            // to DumpRecycling.
            else
            {
              // we want to move to DumpRecycling state
              NextState = DumpRecycling;

              // mark that we are taking a transition (for internal
              // transitions, skip changing MakeTransition)
              MakeTransition = true;

              // we do not want to enter DumpRecycling with history, so we
              // will leave the default to ES_ENTRY (make no change)
              //EntryEventKind.EventType = ES_ENTRY_HISTORY;

              // consume this event for the upper state machines
              ReturnEvent.EventType = ES_NO_EVENT;
            }
          }
          break;
        }
      }
    }
    break;  // end switch on CurrentState case for DumpTrash
  }

  // if we are making a state transition
  if (MakeTransition == true)
  {
    // Execute exit function for current state
    CurrentEvent.EventType = ES_EXIT;
    RunGameTasksSM(CurrentEvent);

    CurrentState = NextState; // Modify state variable

    // Execute entry function for new state (this defaults to ES_ENTRY)
    RunGameTasksSM(EntryEventKind);
  }
  return ReturnEvent;
}

/****************************************************************************
 Function
     StartGameTasksSM

 Parameters
     None

 Returns
     None

 Description
     Does any required initialization for this state machine

 Notes

 Author
     Bibit Bianchini, 2/21/2019
****************************************************************************/
void StartGameTasksSM(ES_Event_t CurrentEvent)
{
  // to implement entry to a history state or directly to a substate
  // you can modify the initialization of the CurrentState variable
  // otherwise just start in the entry state every time the state machine
  // is started
  if (CurrentEvent.EventType != ES_ENTRY_HISTORY)
  {
    CurrentState = ENTRY_STATE;
  }
  // call the entry function (if any) for the ENTRY_STATE
  RunGameTasksSM(CurrentEvent);
}

/****************************************************************************
 Function
     QueryGameTasksSM

 Parameters
     None

 Returns
     GameTasksState_t The current state of the state machine

 Description
     returns the current state of the GameTasks state machine

 Notes

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

/****************************************************************************
 Collection of Functions
    QueryBeaconRearFreq
    SetBeaconRearFreq

 Parameters
   None
   uint16_t : the frequency

 Returns
   uint16_t : the frequencies
   None

 Description
   Returns the frequencies that the beacon detector should be looking for,
   or allows you to set the frequency.

 Notes

 Author
   Bibit Bianchini, 2/25/2019
****************************************************************************/
uint16_t QueryBeaconRearFreq(void)
{
  return BeaconRearFreq;
}

void SetBeaconRearFreq(uint16_t NewFreq)
{
  BeaconRearFreq = NewFreq;
}

/***************************************************************************
 private functions
 ***************************************************************************/
/****************************************************************************
 Function
     DuringCollectBalls

 Parameters
     ES_Event_t Event

 Returns
     ES_Event_t

 Description
     During function for the CollectBalls state.  Processes ES_ENTRY and
     ES_EXIT events, and calls lower state machines.

 Notes

 Author
     Bibit Bianchini, 2/21/2019
****************************************************************************/
static ES_Event_t DuringCollectBalls(ES_Event_t Event)
{
  ES_Event_t ReturnEvent = Event; // assume no re-mapping or consumption

  // process ES_ENTRY, ES_ENTRY_HISTORY & ES_EXIT events
  if ((Event.EventType == ES_ENTRY) ||
      (Event.EventType == ES_ENTRY_HISTORY))
  {
    // if we got in here with history, it is because we avoided a robot.
    // now, we don't want to go deeper with history, so we will change the
    // event to be ES_ENTRY only
    Event.EventType = ES_ENTRY;

    // post MOTOR_COMMAND with EventParam FORWARD
    ES_Event_t PostEvent;
    PostEvent.EventType = MOTOR_COMMAND;
    PostEvent.EventParam = FORWARD;
    PostMotorSM(PostEvent);

    // now start any lower level machines that run in this state
    StartCollectBallsSM(Event);
  }
  else if (Event.EventType == ES_EXIT)
  {
    // on exit, give the lower levels a chance to clean up first
    RunCollectBallsSM(Event);

    // no tasks to do for exit from CollectBalls
  }
  else
  // do the 'during' function for this state
  {
    // run any lower level state machine
    ReturnEvent = RunCollectBallsSM(Event);

    // do any activity that is repeated as long as we are in this state
  }
  // return either Event, if you don't want to allow the lower level machine
  // to remap the current event, or ReturnEvent if you do want to allow it.
  return ReturnEvent;
}

/****************************************************************************
 Function
     DuringDumpRecycling

 Parameters
     ES_Event_t Event

 Returns
     ES_Event_t

 Description
     During function for the DumpRecycling state.  Processes ES_ENTRY and
     ES_EXIT events, and calls lower state machines.

 Notes

 Author
     Bibit Bianchini, 2/21/2019
****************************************************************************/
static ES_Event_t DuringDumpRecycling(ES_Event_t Event)
{
  ES_Event_t ReturnEvent = Event; // assume no re-mapping or consumption

  // process ES_ENTRY, ES_ENTRY_HISTORY & ES_EXIT events
  if ((Event.EventType == ES_ENTRY) ||
      (Event.EventType == ES_ENTRY_HISTORY))
  {
    // if we got in here with history, it is because we avoided a robot.
    // now, we don't want to go deeper with history, so we will change the
    // event to be ES_ENTRY only
    Event.EventType = ES_ENTRY;

    // start turning clockwise
    ES_Event_t PostEvent;
    PostEvent.EventType = MOTOR_COMMAND;
    PostEvent.EventParam = CW;
    PostMotorSM(PostEvent);

    // set BeaconRearFreq to north landfill beacon frequency
    BeaconRearFreq = NorthLandfillFreq;

    // enable interrupts for rear beacon sensor
    EnableRearBeaconInterrupts();

    // now start any lower level machines that run in this state
    StartDumpRecyclingSM(Event);
  }
  else if (Event.EventType == ES_EXIT)
  {
    // on exit, give the lower levels a chance to clean up first
    RunDumpRecyclingSM(Event);

    // no tasks to do for exit from DumpRecycling
  }
  else
  // do the 'during' function for this state
  {
    // run any lower level state machine
    ReturnEvent = RunDumpRecyclingSM(Event);

    // do any activity that is repeated as long as we are in this state
  }
  // return either Event, if you don't want to allow the lower level machine
  // to remap the current event, or ReturnEvent if you do want to allow it.
  return ReturnEvent;
}

/****************************************************************************
 Function
     DuringDumpTrash

 Parameters
     ES_Event_t Event

 Returns
     ES_Event_t

 Description
     During function for the DumpTrash state.  Processes ES_ENTRY and ES_EXIT
     events, and calls lower state machines.

 Notes

 Author
     Bibit Bianchini, 2/21/2019
****************************************************************************/
static ES_Event_t DuringDumpTrash(ES_Event_t Event)
{
  ES_Event_t ReturnEvent = Event; // assume no re-mapping or consumption

  // process ES_ENTRY, ES_ENTRY_HISTORY & ES_EXIT events
  if ((Event.EventType == ES_ENTRY) ||
      (Event.EventType == ES_ENTRY_HISTORY))
  {
    // if we got in here with history, it is because we avoided a robot.
    // now, we don't want to go deeper with history, so we will change the
    // event to be ES_ENTRY only
    Event.EventType = ES_ENTRY;

    // start turning clockwise
    ES_Event_t PostEvent;
    PostEvent.EventType = MOTOR_COMMAND;
    PostEvent.EventParam = CW;
    PostMotorSM(PostEvent);

    // set BeaconRearFreq based on which team we are
    if (GetOurLandfillFreq() == NorthLandfillFreq)
    {
      // then we want to drive to the east recycling center
      BeaconRearFreq = EastRecyclingFreq;
    }
    else
    {
      // otherwise we want to drive to the west recycling center
      BeaconRearFreq = WestRecyclingFreq;
    }

    // enable interrupts for rear beacon sensor
    EnableRearBeaconInterrupts();

    // now start any lower level machines that run in this state
    StartDumpTrashSM(Event);
  }
  else if (Event.EventType == ES_EXIT)
  {
    // on exit, give the lower levels a chance to clean up first
    RunDumpTrashSM(Event);

    // no tasks to do for exit from DumpTrash
  }
  else
  // do the 'during' function for this state
  {
    // run any lower level state machine
    ReturnEvent = RunDumpTrashSM(Event);

    // do any activity that is repeated as long as we are in this state
  }
  // return either Event, if you don't want to allow the lower level machine
  // to remap the current event, or ReturnEvent if you do want to allow it.
  return ReturnEvent;
}

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