/****************************************************************************
 Module
   BallSortingService.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 "BallSortingService.h"

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

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

//Other Stuff
#include "I2CService.h"
#include "ServoModule.h"
#include "CompassService.h"
#include "GameplayHSM.h"
#include "GameControlService.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 ENTER_POST ((MyPriority << 3) | 0)
#define ENTER_RUN ((MyPriority << 3) | 1)
#define ENTER_TIMEOUT ((MyPriority << 3) | 2)

//Define Servo positions
#define SERVO_CENTER
#define SERVO_RECYCLE
#define SERVO_LANDFILL

//Define max storage
#define MAX_RECYCLE 5
#define MAX_LANDFILL 6

// #define ALL_BITS (0xff<<2)   Moved to ES_Port.h
/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this service.They should be functions
   relevant to the behavior of this service
*/

static uint8_t CategorizeColor(void);

/*---------------------------- Module Variables ---------------------------*/
// with the introduction of Gen2, we need a module level Priority variable
static uint8_t    MyPriority;
// add a deferral queue for up to 3 pending deferrals +1 to allow for ovehead
static ES_Event_t DeferralQueue[3 + 1];
static BSState_t  CurrentState;
//Define counter values
static uint8_t    RecycleCount;
static uint8_t    LandfillCount;

//Define color
static uint8_t  CurrentColor = 0;
//Define game inactive or active flag
static bool     GameActive = false;
//Define boolean variable for ball storing
static bool     HoldBallRecycle;
//Set boolean variable for identifying if recycling center is full
static bool     FullRecycle = false;
static bool     FullLandfill = false;

/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitBallSortingService

 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
     J. Edward Carryer, 01/16/12, 10:00
****************************************************************************/
bool InitBallSortingService(uint8_t Priority)
{
  ES_Event_t ThisEvent;

  MyPriority = Priority;
  /********************************************
   in here you write your initialization code
   *******************************************/
  // initialize deferral queue for testing Deferal function
  ES_InitDeferralQueueWith(DeferralQueue, ARRAY_SIZE(DeferralQueue));
  SetupAllServoFrequencies();
  SetServoStartingPositions();
  //Reset ball counters
  LandfillCount = 0;
  RecycleCount = 0;
  //Set servo to neutral position
  //PWMSERVO(8,SERVO_CENTER); //PLACEHOLDER//
  //Set next state to Initial pseudostate
  CurrentState = InitBSState;
  // post the initial transition event
  ThisEvent.EventType = ES_INIT;
  printf("Sorter");
  if (ES_PostToService(MyPriority, ThisEvent) == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

/****************************************************************************
 Function
     PostBallSortingService

 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 PostBallSortingService(ES_Event_t ThisEvent)
{
  return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunBallSortingService

 Parameters
   ES_Event : the event to process

 Returns
   ES_Event, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   add your description here
 Notes

 Author
   J. Edward Carryer, 01/15/12, 15:23
****************************************************************************/
ES_Event_t RunBallSortingService(ES_Event_t ThisEvent)
{
  ES_Event_t  ReturnEvent;
  ReturnEvent.EventType = ES_NO_EVENT; // assume no errors
  ES_Event_t  NextEvent;

  // we will force the ball sorting service to always sort, no matter if
  // the game has started or not
  GameActive = true;
  
  switch (CurrentState)
  {
    case InitBSState:
    {
      //Initial State
      if (ThisEvent.EventType == ES_INIT)
      {
        //Reset ball counters
        LandfillCount = 0;
        RecycleCount = 0;
        printf("Setting values to 0\n\r");
        //Set servo to neutral position
        ResetSorter();
        //Set for No Ball
        CurrentState = NoBall;
      }
    }
    break;
    case NoBall:
    {
      puts("No ball\n\r");
      if ((ThisEvent.EventType == BALL_AT_COLOR_SENSOR) && (GameActive == false))
      {
        //Ball detected, but game is idle, keep waiting
        printf("Game Stopped\n\r");
        CurrentState = NoBall;
      }
      else if ((ThisEvent.EventType == BALL_AT_COLOR_SENSOR) && (GameActive == true) && (ThisEvent.EventParam == 1)
          && (FullRecycle == false))
      {
        //Ball is recycling, store in bin 1
        //Set Servo to recycling direction
        //PLACEHOLDER for final integration
        SortRecycle();
        //Increase Recycle count
        RecycleCount++;
        printf("Recycle recieved %d ball collected\n\r", RecycleCount);
        //Check if Recycle Count has gotten to the max value
        if (RecycleCount == MAX_RECYCLE)
        {
          printf("RECYCLE FULL, PLEASE EMPTY");
          FullRecycle = true;
          //PLACEHOLDER for gameplay
          NextEvent.EventType = RECYCLE_FULL;
          PostBallSortingService(NextEvent);
        }
        //Start Timer for 500ms
        ES_Timer_InitTimer(ColorServoTimer, 500);
        //Set next state to Emptying
        CurrentState = Emptying;
      }
      else if ((ThisEvent.EventType == BALL_AT_COLOR_SENSOR) && (GameActive == true) && (ThisEvent.EventParam == 0)
          && (FullLandfill == false))
      {
        //Ball is trash, store in bin 2
        //Set Servo to landfill direction
        SortTrash();
        //Increase Landfill count
        LandfillCount++;
        printf("Landfill recieved %d ball collected\n\r", LandfillCount);
        //Check if Landfill Count has gotten to the max value
        if (LandfillCount == MAX_LANDFILL)
        {
          //Set Max recycle flag active
          printf("LANDFILL FULL, PLEASE EMPTY");
          FullLandfill = true;
          //PLACEHOLDER for gameplay post
          NextEvent.EventType = TRASH_FULL;
          PostBallSortingService(NextEvent);
        }
        //Start Timer for 500ms
        ES_Timer_InitTimer(ColorServoTimer, 500);
        //Set next state to Emptying
        CurrentState = Emptying;
      }
      else if ((ThisEvent.EventType == BALL_AT_COLOR_SENSOR) && (GameActive == true) && (ThisEvent.EventParam == 1)
          && (FullRecycle == true))
      {
        printf("Recycle Full\n\r");
        //Recycling is full,store ball type
        HoldBallRecycle = true;
        CurrentState = WaitingForBall;
      }
      else if ((ThisEvent.EventType == BALL_AT_COLOR_SENSOR) && (GameActive == true) && (ThisEvent.EventParam == 0)
          && (FullLandfill == true))
      {
        printf("Trash Full\n\r");
        //Landfill is full,store ball type
        HoldBallRecycle = false;
        CurrentState = WaitingForBall;
      }
      else if (ThisEvent.EventType == GAME_OVER)
      {
        printf("Game over, emptying\n\r");
        //Return Servo to normal position
        ResetSorter();
        //Reset ball counters
        LandfillCount = 0;
        FullLandfill = false;
        RecycleCount = 0;
        FullRecycle = false;
        //Return to no ball state
        CurrentState = NoBall;
      }
      else if ((ThisEvent.EventType == RECYCLE_EMPTY) && (GameActive == true))
      {
        //Reset recycling ball counter
        puts("Emptying recycle");
        RecycleCount = 0;
        FullRecycle = false;
      }
      else if ((ThisEvent.EventType == TRASH_EMPTY) && (GameActive == true))
      {
        //Reset recycling ball counter
        puts("Emptying trash");
        LandfillCount = 0;
        FullLandfill = false;
      }
    }
    break;
    case WaitingForBall:
    {
      puts("Wait 4 ball\n\r");
      if (ThisEvent.EventType == GAME_OVER)
      {
        //Return Servo to normal position
        ResetSorter();
        //Reset ball counters
        LandfillCount = 0;
        RecycleCount = 0;
        //Return to no ball state
        CurrentState = NoBall;
      }
      else if ((ThisEvent.EventType == RECYCLE_EMPTY) && (HoldBallRecycle == false))
      {
        //Reset ball Counter
        RecycleCount = 0;
        FullRecycle = false;
      }
      else if ((ThisEvent.EventType == RECYCLE_EMPTY) && (HoldBallRecycle == true))
      {
        //Reset ball Counter
        RecycleCount = 0;
        //Reset FullRecycle flag
        FullRecycle = false;
        //Ball is recycling, store in bin 1
        SortRecycle();
        //Add 1 to counter
        RecycleCount++;
        //Start 500 ms timer
        ES_Timer_InitTimer(ColorServoTimer, 500);
        //Set next state to Emptying
        CurrentState = Emptying;
      }
      else if ((ThisEvent.EventType == TRASH_EMPTY) && (HoldBallRecycle == true))
      {
        //Reset ball Counter
        LandfillCount = 0;
        FullLandfill = false;
      }
      else if ((ThisEvent.EventType == TRASH_EMPTY) && (HoldBallRecycle == false))
      {
        //Reset ball Counter
        LandfillCount = 0;
        //Reset FullLandfill flag
        FullLandfill = false;
        //Ball is Landfill, store in bin 0
        SortTrash();
        //Add 1 to counter
        LandfillCount++;
        //Start 500 ms timer
        ES_Timer_InitTimer(ColorServoTimer, 500);
        //Set next state to Emptying
        CurrentState = Emptying;
      }
    }
    break;
    case Emptying:
    {
      puts("Emptying\n\r");
      //Timeout for servo
      if ((ThisEvent.EventType == ES_TIMEOUT) && (ThisEvent.EventParam == ColorServoTimer))
      {
        printf("Servo returns\n\r");
        //Return Servo to normal position
        ResetSorter();
        //Return to no ball state
        CurrentState = NoBall;
      }
      else if (ThisEvent.EventType == TRASH_EMPTY)
      {
        //Reset ball Counter
        LandfillCount = 0;
        FullLandfill = false;
      }
      else if (ThisEvent.EventType == RECYCLE_EMPTY)
      {
        //Reset ball Counter
        RecycleCount = 0;
        FullRecycle = false;
      }
      else if (ThisEvent.EventType == GAME_OVER)
      {
        printf("Servo returns\n\r");
        //Return Servo to normal position
        ResetSorter();
        //Reset ball counters
        LandfillCount = 0;
        RecycleCount = 0;
        FullRecycle = false;
        FullLandfill = false;
        //Return to no ball state
        CurrentState = NoBall;
      }
    }
    break;
    default:
    {}
    break;
  }

  return ReturnEvent;
}

bool QueryNoBall(void)
{
  if (CurrentState == NoBall)
  {
    return true;
  }
  else
  {
    return false;
  }
}

/***************************************************************************
 private functions
 ***************************************************************************/

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