/****************************************************************************
 Module
   CompassService.c

 Revision
   1.0.1

 Description
   This is the highest level module for ME 218B Lab 8.  Implements a flat
   state machine under the Gen2 Events and Services Framework.

 Notes
   Team 3

 History
 When           Who     What/Why
 -------------- ---     --------
 02/04/2019     pablo   created file
****************************************************************************/
/*----------------------------- 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
*/
// This module
#include "CompassService.h"

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

// Hardware
#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 other files
#include "GameControlService.h"
#include "GameplayHSM.h"

/*----------------------------- Module Defines ----------------------------*/
#define BitsPerNibble 4

//Void byte for sending to Compass and receiving data
#define VOIDBYTE 0x00

//Pins for preprogrammed queries
#define QUERYCOLOR 0XD2
#define QUERYECOP 0xB4
#define QUERYSTATUS 0x78
#define QUERYRECYCLE 0x69

//Mask Defines
#define TEAMBYTE 0x01
#define TEAMCOLOR 0x0E
#define TEAMFREQ 0xF0
#define STATUS 0x03
#define ERECYCLE 0x1C
#define WRECYCLE 0xE0

/*---------------------------- Module Functions ---------------------------*/
/* prototypes for private functions for this machine.They should be functions
   relevant to the behavior of this state machine
*/
static void InitSPIComms(void);
static void TransmitSPI(uint8_t SendData);
static uint16_t ReadSPI(void);
static void InitSPIComms(void);

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

//Save query values
static uint8_t  TeamAssigned;
static uint8_t  ColorAssigned;
static uint8_t  FreqAssigned;
static uint8_t  EastRecycleColor;
static uint8_t  WestRecycleColor;
static uint8_t  GameState;

//Flag for game status
static bool GameActive = false;

/****************************************************************************
 Function
     InitializeCompassService

 Parameters
     void

 Returns
     void

 Description
     Initializes SPI communications, and relevant hardware ports
 Notes

 Author
     Pablo Martinez Alanis
****************************************************************************/
bool InitCompassService(uint8_t Priority)
{
  ES_Event_t ThisEvent;

  MyPriority = Priority;
  /********************************************
   in here you write your initialization code
   ********************************************/
  //Finish initializing SPI communication module
//InitSPIComms();

  //Enable Global interrupts, may be replaced in global initialization variable
  //__enable_irq();
  ThisEvent.EventType = ES_INIT;
  if (ES_PostToService(MyPriority, ThisEvent) == true)
  {
    return true;
  }
  else
  {
    return false;
  }
}

/****************************************************************************
 Function
     PostCompassService

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

/****************************************************************************
 Function
    RunCompassService

 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 RunCompassService(ES_Event_t ThisEvent)
{
  ES_Event_t  ReturnEvent;
  ES_Event_t  NextEvent;
  ReturnEvent.EventType = ES_NO_EVENT; // assume no errors

  switch (CurrentState)
  {
    case InitCState:
    {
      //Start with initial transition event
      if (ThisEvent.EventType == ES_INIT)
      {
        //Set transition to wait for REG byte state
        CurrentState = WaitforREG;
        //Initialize delay timer
        ES_Timer_InitTimer(CompassDelayTimer, 2);
      }
    }
    break;
    case WaitforREG:
    {
      //Waiting for registration byte
      if (ThisEvent.EventType == TEAM_SELECT)
      {
        //Team select byte received, send to SPI communication function
        TransmitSPI(ThisEvent.EventParam);
        //Transition to wait for ACK byte state
        CurrentState = WaitForACK;
      }
    }
    break;
    case WaitForACK:
    {
      //Waiting for acknowledge byte from EOT
      if (ThisEvent.EventType == DATA_RECEIVED)
      {
        //Received byte from COMPASS, post REG_Complete event to GameControlService
        NextEvent.EventType = REG_COMPLETE;
        PostGameControlService(NextEvent);
        //Initialize query timer
        ES_Timer_InitTimer(CompassQueryTimer, 500);
        //Transition to query timer state
        CurrentState = WaitForQuery;
      }
    }
    break;
    case WaitForQuery:
    {
      //Waiting for Timeout
      if ((ThisEvent.EventType == ES_TIMEOUT) && (ThisEvent.EventParam == CompassQueryTimer))
      {
        //Querying Timeout
        //printf("Sending data to SPI\n\r");
        //Timeout happened, send query for 0XD2
        TransmitSPI(QUERYCOLOR);
        //Transition into wait for TEAM byte
        CurrentState = WaitForTEAM;
      }
    }
    break;
    case WaitForTEAM:
    {
      //Waiting for EOT
      if (ThisEvent.EventType == DATA_RECEIVED)
      {
        //Received data event, save to module variables
        //Mask Team Selection
        TeamAssigned = (ThisEvent.EventParam & TEAMBYTE);
        //Mask Color selection (shift 1 bit to the left)
        ColorAssigned = (ThisEvent.EventParam & TEAMCOLOR) >> 1;
        //Mask Frequency selection (shift 4 bits to the left
        FreqAssigned = (ThisEvent.EventParam & TEAMFREQ) >> 4;
        //Querying values
        //Start delay timer
        ES_Timer_InitTimer(CompassDelayTimer, 500);
        //Switch to next case
        CurrentState = WaitForDelay;
      }
    }
    break;
    case WaitForDelay:
    {
      //Waiting for timeout
      if ((ThisEvent.EventType == ES_TIMEOUT) && (ThisEvent.EventParam == CompassDelayTimer))
      {
        //Send query for Status
        TransmitSPI(QUERYSTATUS);
        //Transition into wait for STAT byte
        CurrentState = WaitForStat;
      }
    }
    break;
    case WaitForStat:
    {
      //Wait for EOT
      if (ThisEvent.EventType == DATA_RECEIVED)
      {
        //Received data event, save to module variables
        //Mask GameState
        //printf("Byte recieved; %d\n\r",ThisEvent.EventParam);
        GameState = (ThisEvent.EventParam & STATUS);
        //Mask East Recycling color (shift 2 bits to the left)
        EastRecycleColor = (ThisEvent.EventParam & ERECYCLE) >> 2;
        //Mask West Recycling color (shift 5 bits to the left
        WestRecycleColor = (ThisEvent.EventParam & WRECYCLE) >> 5;
        //Check if game has started or ended
        if ((GameState == 1) && (GameActive == false))
        {
          //Game has started, post to relevant machines:
          //   - GameControl, Gameplay, Compass
          NextEvent.EventType = GAME_STARTED;
          PostGameControlService(NextEvent);
          PostGameplayMasterSM(NextEvent);
          PostCompassService(NextEvent);
          GameActive = true;
        }
        else if ((GameState == 2) && (GameActive == true))
        {
          printf("Game Over");
          //Game has ended,  post to relevant machines:
          //   - GameControl, Gameplay, Compass, BallSorting
          NextEvent.EventType = GAME_OVER;
          PostGameControlService(NextEvent);
          PostGameplayMasterSM(NextEvent);
          PostCompassService(NextEvent);
          GameActive = false;
        }
        //Initialize query timer
        ES_Timer_InitTimer(CompassQueryTimer, 500);
        //Transition to query timer (and repeat process)
        CurrentState = WaitForQuery;
      }
    }
  }
  return ReturnEvent;
}

/***************************************************************************
 Getter functions
 ***************************************************************************/
uint8_t GetTeamAssignment(void)
{
  return TeamAssigned;
}

uint8_t GetColorAssignment(void)
{
  return ColorAssigned;
}

uint16_t GetFreqAssignment(void)
{
  uint16_t FrequencyAssigned = GetOurRecycleBeaconFreq();
  if (FrequencyAssigned == WestRecycleColor)
  {
    FrequencyAssigned = WestAcceptedFreq;
  }
  else if (FrequencyAssigned == EastRecycleColor)
  {
    FrequencyAssigned = EastAcceptedFreq;
  }
  return FrequencyAssigned;
}

uint8_t GetEastRecycleColor(void)
{
  return EastRecycleColor;
}

uint8_t GetWestRecycleColor(void)
{
  return WestRecycleColor;
}

bool QueryGameStatus(void)
{
  return GameActive;
}

/***************************************************************************
 private functions
 ***************************************************************************/
/****************************************************************************
 Function
     TransmitSPI

 Parameters
     uint8_t SendData data to send to the Command module

 Returns
     void

 Description
     Writes to the communication value the data to write
 Notes

 Author
     Pablo Martinez Alan
****************************************************************************/
static void TransmitSPI(uint8_t SendData)
{
  // Writes SendData to SSI Data Register
  HWREG(SSI0_BASE + SSI_O_DR) = SendData;
  //Sends trash bits for communication purposes
  HWREG(SSI0_BASE + SSI_O_DR) = VOIDBYTE;
  HWREG(SSI0_BASE + SSI_O_DR) = VOIDBYTE;
  //HWREG(SSI0_BASE + SSI_O_DR) = VOIDBYTE;
  // Activates Local Interrupt to send data
  HWREG(SSI0_BASE + SSI_O_IM) |= SSI_IM_TXIM;

  // Debbuging aids
  //printf("DataSent %x\n\r",SendData);
  //printf("IE");
}

/****************************************************************************
 Function
     TransmitSPI

 Parameters
     uint8_t SendData data to send to the Command module

 Returns
     void

 Description
     Writes to the communication value the data to write
 Notes

 Author
     Pablo Martinez Alanis
****************************************************************************/
static uint16_t ReadSPI(void)
{
  uint16_t SPIResponse = 0x0000;
  //Read first trash byte, should be 0x00
  HWREG(SSI0_BASE + SSI_O_DR);
  //Read second trash byte, should be 0xFF
  HWREG(SSI0_BASE + SSI_O_DR);
  // Writes first data byte from the SPI
  SPIResponse = HWREG(SSI0_BASE + SSI_O_DR);
  //printf("SPI Function call: %X  ",SPIResponse);
  //Write the second value
  //SPIResponse|=HWREG(SSI0_BASE + SSI_O_DR);
  return SPIResponse;
}

/****************************************************************************
 Function
    ReceiveSPI

 Parameters
   void

 Returns
   void

 Description
   Interrupt Service Routine for receiving interrupt after EOT

 Notes

 Author
   Pablo Martinez Alanis
****************************************************************************/
void ReceiveSPI(void)
{
  // Disables Local interrupts
  HWREG(SSI0_BASE + SSI_O_IM) &= (~SSI_IM_TXIM);
  //Sends EOT Event to Cpmpass Service
  ES_Event_t PostEvent;
  PostEvent.EventType = DATA_RECEIVED;
  PostEvent.EventParam = ReadSPI();
  //printf("%x0\n\r",PostEvent.EventParam);
  //Posts to this event the SPI received value
  PostCompassService(PostEvent);

  //printf("Received SPI: %d \n\r",LastSPICommand);
}

static void InitSPIComms(void)
{
  //Activates clock to GPIO PortA
  HWREG(SYSCTL_RCGCGPIO) |= SYSCTL_RCGCGPIO_R0;

  // Enables clock to SSI module 0
  HWREG(SYSCTL_RCGCSSI) |= SYSCTL_RCGCSSI_R0;

  // Waits for GPIO port to be ready
  while ((HWREG(SYSCTL_PRGPIO) & BIT0HI) != BIT0HI)
  {}

  // Programs GPIO for alternate functions
  HWREG(GPIO_PORTA_BASE + GPIO_O_AFSEL) |= (BIT2HI | BIT3HI | BIT4HI | BIT5HI);

  // PA2=SSIOClk, PA3=SSIOFss, PA4=SSIORx, PA5 SSIOTx
  // Writes in GPIOCTL to select desired functions
  HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL) =
      (HWREG(GPIO_PORTA_BASE + GPIO_O_PCTL) & 0xff0000ff) + (2 << BitsPerNibble * 2)
      + (2 << BitsPerNibble * 3) + (2 << BitsPerNibble * 4) + (2 << BitsPerNibble * 5);

  // Programs PA2 to PA5 for digital I/O
  HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= (BIT2HI | BIT3HI | BIT4HI | BIT5HI);

  // Programs PA2 to PA4 data directions
  // (PA2, PA3 and PA5 are outputs, PA4 is input)
  HWREG(GPIO_PORTA_BASE + GPIO_O_DIR) |= (BIT2HI | BIT3HI | BIT5HI);
  HWREG(GPIO_PORTA_BASE + GPIO_O_DIR) &= (BIT4LO);

  // Programs Pull-up for the clock line for SPI mode 3
  HWREG(GPIO_PORTA_BASE + GPIO_O_PUR) |= (BIT2HI);

  //Waits for SSI0 to be ready
  while ((HWREG(SYSCTL_RCGCSSI) & SYSCTL_RCGCSSI_R0) != SYSCTL_RCGCSSI_R0)
  {}

  // Disables SSI0 0
  HWREG(SSI0_BASE + SSI_O_CR1) &= (~SSI_CR1_SSE);

  // Select Master mode, and TXRIS for End of Transmission
  HWREG(SSI0_BASE + SSI_O_CR1) &= (~SSI_CR1_MS);
  HWREG(SSI0_BASE + SSI_O_CR1) |= (SSI_CR1_EOT);

  // Configures SSI clock source to be the system clock
  HWREG(SSI0_BASE + SSI_O_CC) = (SSI_CC_CS_SYSPLL);

  // Configures clock prescaler (Bit Rate 15 kHz) CPSR = 132
  HWREG(SSI0_BASE + SSI_O_CPSR) = HWREG(SSI0_BASE + SSI_O_CPSR) & (0xFFFFFF00) + 132;
  // Set SCR to 19
  HWREG(SSI0_BASE + SSI_O_CR0) = ((HWREG(SSI0_BASE + SSI_O_CR0) + (19 << SSI_CR0_SCR_S)));

  // Configure SPH = 1 because data is captured at the second edge
  HWREG(SSI0_BASE + SSI_O_CR0) |= SSI_CR0_SPH;

  // Configure SPO = 1 because idle state is high
  HWREG(SSI0_BASE + SSI_O_CR0) |= SSI_CR0_SPO;

  // Freescale SPI Mode
  HWREG(SSI0_BASE + SSI_O_CR0) = ((HWREG(SSI0_BASE + SSI_O_CR0) &
      (~SSI_CR0_FRF_M)) | SSI_CR0_FRF_MOTO);

  // Sending 8 bit data between command generator & TIVA
  HWREG(SSI0_BASE + SSI_O_CR0) = ((HWREG(SSI0_BASE + SSI_O_CR0) &
      (~SSI_CR0_DSS_M)) | SSI_CR0_DSS_8);

  // Locally enable interrupts (TXIM in SSIIM)
  HWREG(SSI0_BASE + SSI_O_IM) |= SSI_IM_TXIM;

  // Enable SSI
  HWREG(SSI0_BASE + SSI_O_CR1) |= (SSI_CR1_SSE);

  // Enable NVIC bit 7 in EN0
  HWREG(NVIC_EN0) |= BIT7HI;

  // Globally enable interrupts
  //__enable_irq(); // commented out because this is done in CommandFSM init
}