/****************************************************************************
 Module
   WelcomeFSM.c

 Revision
   1.0.1

 Description
   This state machine specifies the behavior of our Arcade in Welcome Mode!

 Notes

****************************************************************************/
/*----------------------------- Include Files -----------------------------*/
/* 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
*/
#include "ES_Configure.h"
#include "ES_Framework.h"

// Libraries
#include "WelcomeFSM.h"
#include "Meteor.h"
#include "Cannon.h"
#include "AirLeakLib.h"
#include "PowerLib.h"
#include "ArcadeFSM.h"
#include "Crisis.h"
#include "Servo.h"

/*----------------------------- Module Defines ----------------------------*/
#define STEP_DELAY 100
#define LED_MOD 2 // should be a power of 2

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/
void StepCannonGauge(void);
void StepLED(void);

/*---------------------------- Module Variables ---------------------------*/
// everybody needs a state variable, you may need others as well.
// type of state variable should match htat of enum in header file
static WelcomeState_t CurrentState;

// with the introduction of Gen2, we need a module level Priority var as well
static uint8_t MyPriority;

// Indiates whether cannon + gauges moving CW (true) or CCW (false)
static bool DirectionFlag;

// For LED timing
static uint16_t counter;


/*------------------------------ Module Code ------------------------------*/
/****************************************************************************
 Function
     InitWelcomeFSM

 Parameters
     uint8_t : the priorty of this service

 Returns
     bool, false if error in initialization, true otherwise

 Description
     Saves away the priority, sets up the initial transition and does any
     other required initialization for this state machine
 Notes

 Author
     RPT, 11/8/19
****************************************************************************/
bool InitWelcomeFSM(uint8_t Priority)
{
  ES_Event_t ThisEvent;

  MyPriority = Priority;
  // put us into the Initial PseudoState
  CurrentState = InitPState;
  // post the initial transition event
  ThisEvent.EventType = ES_INIT;
  if (ES_PostToService(MyPriority, ThisEvent) == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

/****************************************************************************
 Function
     PostWelcomeFSM

 Parameters
     EF_Event_t ThisEvent , the event to post to the queue

 Returns
     boolean False if the Enqueue operation failed, True otherwise

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

 Author
     RPT, 11/8/19
****************************************************************************/
bool PostWelcomeFSM(ES_Event_t ThisEvent)
{
  return ES_PostToService(MyPriority, ThisEvent);
}

/****************************************************************************
 Function
    RunWelcomeFSM

 Parameters
   ES_Event_t : the event to process

 Returns
   ES_Event_t, ES_NO_EVENT if no error ES_ERROR otherwise

 Description
   add your description here
 Notes
   uses nested switch/case to implement the machine.
 Author
   RPT, 11/8/19
****************************************************************************/
ES_Event_t RunWelcomeFSM(ES_Event_t ThisEvent)
{
  ES_Event_t ReturnEvent;
  ES_Event_t NewEvent;
  ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

  switch (CurrentState)
  {
    case InitPState:        // If current state is initial Psedudo State
    {
      if (ThisEvent.EventType == ES_INIT)    // only respond to ES_Init
      {
        CurrentState = Idle_W;
        NewEvent.EventType = Go_Welcome_Mode;
        PostWelcomeFSM(NewEvent);
      }
    }
    break;

    case Idle_W:
    {
      switch (ThisEvent.EventType)
      {
        case Go_Welcome_Mode: 
        {
          // set DirectionFlag to true (start moving CW)
          DirectionFlag = true;
          // Move the gauges to empty
          AirLeak_MoveGauge(0);
          PowerLib_MoveGauge(0);
          // Move cannon fully to one side
          Cannon_ManualPosition(0);
          // Turn all LEDs off
          Meteor_ClearAll();
          // Turn off Crisis LEDs
          Crisis_AirLeak(Off);
          Crisis_Meteor(Off);
          Crisis_Power(Off);
          // Reset Global Timer
          Servo_MoveTimer(0);
          printf("\rZereod\r\n");
          // set CurrentState to Welcome
          CurrentState = Welcome;
          // Start timer
          ES_Timer_InitTimer(WELCOME_TIMER, STEP_DELAY);
        }
        break;

        default:
          ;
      }
    }
    break;

    case Welcome:
    {
      switch (ThisEvent.EventType)
      {
        case Tot_Inserted: // define this in ES_Configure
        {
          Meteor_ClearAll();
          Crisis_Meteor(Off);
          Crisis_AirLeak(Off);
          Crisis_Power(Off);
          CurrentState = Idle_W;
        }
        break;

        case ES_TIMEOUT:
        //  printf("welcome");
          // turn off all LEDs
          Meteor_ClearAll();
          // Step cannon
          // Step o2 gauge
          // Step power gauge
          StepCannonGauge();
          // if counter % LED_MOD is 0
          if ( (counter % LED_MOD) == 0)
          {
            StepLED();
          }
          // init timer
          ES_Timer_InitTimer(WELCOME_TIMER, STEP_DELAY);
          // increment counter
          counter = counter + 1;

        default:
          
        
          ;
      }
    }
    break;
    default:
      ;
  }
  return ReturnEvent;
}

WelcomeState_t QueryWelcomeState(void)
{
  return(CurrentState);
}

/***************************************************************************
 private functions
 ***************************************************************************/
void StepCannonGauge(void)
{
  // NOTE: asumming cannon & gauges all at same position
  // get current cannon position
  uint8_t CurrentCannonPosition;
  CurrentCannonPosition = QueryCannonPosition();
  // initialize variable NewCannonPosition
  uint8_t NewCannonPosition;

  // if DirectionFlag = true (moving CW)
  if (DirectionFlag == true)
  {
    // if current servo pos == 100
    if (CurrentCannonPosition == 100)
    {
      // set DirectionFlag to false
      DirectionFlag = false;
      // decrement NewPosition
      NewCannonPosition = CurrentCannonPosition - 1;
      // move cannon + gauges to NewPosition
      Cannon_ManualPosition(NewCannonPosition);
      AirLeak_MoveGauge(NewCannonPosition);
      PowerLib_MoveGauge(NewCannonPosition);
    }
    else
    {
      // increment NewPosition
      NewCannonPosition = CurrentCannonPosition + 1;
      // move cannon + gauges to NewPosition
      Cannon_ManualPosition(NewCannonPosition);
      AirLeak_MoveGauge(NewCannonPosition);
      PowerLib_MoveGauge(NewCannonPosition);
    }
  }
  else
  {
    // if CurrentPosition = 0
    if (CurrentCannonPosition == 0)
    {
      // set DirectionFlag to true
      DirectionFlag = true;
      // increment NewPosition
      NewCannonPosition = CurrentCannonPosition + 1;
      // move cannon + gauges to NewPosition
      Cannon_ManualPosition(NewCannonPosition);
      AirLeak_MoveGauge(NewCannonPosition);
      PowerLib_MoveGauge(NewCannonPosition);

    }
    else {
      // decrement NewPosition
      NewCannonPosition = CurrentCannonPosition - 1;
      // move cannon + gauges to NewPosition
      Cannon_ManualPosition(NewCannonPosition);
      AirLeak_MoveGauge(NewCannonPosition);
      PowerLib_MoveGauge(NewCannonPosition);
    }
  }
}

void StepLED(void)
{
  // create variables to keep tack of which LED in which bank is on
  static uint8_t Bank0LED;
  static uint8_t Bank1LED;
  static uint8_t Bank2LED;

  if (counter == 0)
  {
    Bank0LED = 1;
    Bank1LED = 2;
    Bank2LED = 3;
  }
  // Turn on the 3 LEDs corresponding to current step
  Meteor_LightLEDBank(0, Bank0LED);
  Meteor_LightLEDBank(1, Bank1LED);
  Meteor_LightLEDBank(2, Bank2LED);

  // update which LED will be on for each bank
  Bank0LED = (Bank0LED%8) + 1;
  Bank1LED = (Bank1LED%8) + 1;
  Bank2LED = (Bank2LED%8) + 1;
}