#define COMMAND                   'c'       // Character to denote COMMAND
#define DATA                      'd'	    // Character to denote DATA
#define RS_LINE                   BIT0HI    // Of Port M
#define ENABLE_LINE		            BIT1HI    // Of Port M
#define INPUT_LINE                BIT2HI    // Of Port M
#define CLOCK_LINE                BIT3HI    // Of Port M
#define PLAYER_ONE_ADDRESS        0x80      // First address to start writing  RED:
#define PLAYER_TWO_ADDRESS        0x8B      // First address to start writing BLUE:
#define TIMER_ADDRESS             0xC8      // First address to start writing the time left
#define PLAYER_ONE_SCORE_ADDRESS  0x87      // First address to start writing Red Score
#define PLAYER_TWO_SCORE_ADDRESS  0x92      // First address to start writing Blue Score
#define WINNER_ADDRESS            0xC4      // First address to start writing who won
#define INSERTCOIN_ADDRESS        0xC4      // First address to start writing "Insert Coin" after game
#define TRIGGER_ADDRESS_TOP       0x84
#define TRIGGER_ADDRESS_BOTTOM    0xC6
#define LAST_ADDRESS              0x93	  // The Hex value of the last address on the LCD
#define DISPLAY_COMMAND           0x18      // The Hex value for the command to shift display left
#define WAIT_LONG                 10        // The long wait period
#define WAIT_SHORT                2         // The short wait period
#define WAIT_MIDDLE               4         // the middle wait period
#define CLOCK_WAIT                300
#define COOP                      BIT7HI
#define VERSUS                    0
#define COOP_ADDRESS             0x84
#define COOP_SCORE_ADDRESS       0x8B


#include <stdio.h>
#include <ME218_C32.h>
#include <timers12.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

/* ###################################################################
**
**  Function Prototypes
**
** ###################################################################
*/

void LCDInit(void);
void LCDputchar(char character);
void LCDWriteString(char stringToWrite [], unsigned int address);
void WriteScore(int p1_score, int p2_score);
void WritePlayerOneScore(int score);
void WritePlayerTwoScore(int score);
void WriteTime(int time);
void LCDGameSetUp(void);
void LCDGameEnd(int red_score, int blue_score, int win_threshold);
void LCDResetGame(void);
void LCDWaitTrigger(void);

void WriteToPorts(char portData);
void LCD_Init_Ports(void);
void wait(unsigned int wait_time);
void SelectRegister(char type);
void PulseEnable(void);
void PulseClock(void);

static unsigned char GameType;

/* ###################################################################
**
**  Lower Level Functions
**
** ###################################################################
*/

/******************************************
Function:       PulseEnable(void)
Description:    This will pulse the enable line
Input:          None
Output:         None
Revised:        PY          11/14/07
*********************************************/
void PulseEnable(void)          
{ 
    wait(4);
    DDRM = DDRM | ENABLE_LINE;    // Set the Enable line to output
    PTM = PTM | ENABLE_LINE;      // Set the Enable line to HI
    wait(4);
    PTM = PTM & ~ENABLE_LINE;     // Set the Enable line to LO
}

/******************************************
Function:       PulseClock(void)
Description:    This will pulse the clock line
Input:          None
Output:         None
Revised:        PY          11/14/07
*********************************************/
void PulseClock(void)
{  
    int a = 0;
    DDRM = DDRM | CLOCK_LINE;   // Set the Clock line to output
    PTM = PTM | CLOCK_LINE;     // Set the Clock Line to HI 
    while (a < CLOCK_WAIT)
    {
      a++;
    }
    PTM = PTM & ~CLOCK_LINE;
}

/******************************************
Function:       SelectRegister(char type)
Description:    Select the register for data or command
Input:          char type, DATA or COMMAND
Output:         None
Revised:        PY          11/14/07
*********************************************/
void SelectRegister(char type)  
{
    DDRM = DDRM | RS_LINE;		    // Set RS line to output
    if (type == DATA) 				    // If the type is DATA
    {
        PTM = PTM | RS_LINE;        // Set RS to HI
    }
    else    											// Otherwise, it is COMMAND
    {
        PTM = PTM & ~RS_LINE;       // Set RS to LO
    }
}

/******************************************
Function:       LCD_Init_Ports(void)
Description:    Initializes the ports that will be used
                with the LCD 
Input:          None
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void LCD_Init_Ports(void) 
{
  DDRM = DDRM | RS_LINE;        
  DDRM = DDRM | ENABLE_LINE;
  DDRM = DDRM | INPUT_LINE;
  DDRM = DDRM | CLOCK_LINE;
  PTM = PTM & ~RS_LINE;
  PTM = PTM & ~ENABLE_LINE;
  PTM = PTM & ~INPUT_LINE;
  PTM = PTM & ~CLOCK_LINE;
}

/******************************************
Function:       wait(unsigned int wait_time)
Description:    waits the specified wait_time in terms of the clock
                ticks of the initialized timer
Input:          unsigned int wait_time
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void wait(unsigned int wait_time) 
{
  unsigned int startTime = TMRS12_GetTime();
  while((TMRS12_GetTime() - startTime) < wait_time) 
  {
  }
}

/******************************************
Function:       WriteToPorts(char portData)
Description:    Writes the char portData to a shift register
                The bits will match up exactly with the shift
                register ports, i.e., bit 7 will be on Q7 of the
                shift register
Input:          unsigned int wait_time
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void WriteToPorts(char portData)
{
    char i, curr_bit;
    int a = 0;
    unsigned char curr_line;
    curr_line = 128;
    DDRM = DDRM | INPUT_LINE;         // Set the INPUT_LINE to output
    
     
    // Bit 0 of portData will be written to Q0, etc. 
    for (i = 7; i >= 0; i--)
    {
        curr_bit = portData & curr_line;
        if (curr_bit == 0)
        {
            PTM = PTM & ~INPUT_LINE;    // Set INPUT_LINE to LO
        }
        else
        {
            PTM = PTM | INPUT_LINE;     // Set INPUT_LINE to HI
        }
        while (a < CLOCK_WAIT)
        {
          a++;
        }
        PulseClock();
        curr_line = curr_line/2;
    }
}

/* ###################################################################
**
**  Higher Level Functions
**
** ###################################################################
*/

/******************************************
Function:       LCDInit(void);
Description:    It will initialize the LCD to take character inputs.
                This function must be run before using the LCD.
Input:          None
Output:         None
Revised:        PY          11/13/07
*********************************************/
void LCDInit(void)
{
    SelectRegister(COMMAND);        // Set to command
    WriteToPorts(0x30);			            // Write command 0x30
    PulseEnable();		            
	  wait(WAIT_LONG);                // Wait at least 4.1 ms
	  WriteToPorts(0x30);			            // Write command 0x30
	  PulseEnable();		            
	  wait(WAIT_SHORT);               // Wait at least 100 microsecs
	  WriteToPorts(0x30);                     // Write command 0x30
	  PulseEnable();                
	  wait(WAIT_MIDDLE);              // Wait at least 1.6 ms
    WriteToPorts(0x08);                     // Write command 0x08
    PulseEnable();                
	  WriteToPorts(0x01);                     // Write command 0x01
	  PulseEnable();                
	  wait(WAIT_LONG);                // Wait at least 4.9 ms
	  WriteToPorts(0x06);                     // Write command 0x06
	  PulseEnable();              
	  WriteToPorts(0x0F);                     // Write command 0x0F
	  PulseEnable();                  // Pulse the enable line
	  WriteToPorts(0x0C);
    PulseEnable(); 
    LCDResetGame();
}

  
/******************************************
Function:       LCDputchar(void)
Description:    It will take the character passed as the input and place
                it in the current position of the blinker. It will then
                scroll the entire display one character to the left.
Input:          char: character to be output
Output:         None
Revised:        PY          11/13/07
*********************************************/
void LCDputchar(char character)
{
    SelectRegister(DATA);					// Set to DATA
    WriteToPorts(character);			// Write the ASCII equivalent to PTT
    PulseEnable();								
}

/******************************************
Function:       LCDWriteString(char stringToWrite [], unsigned int address)
Description:    Will write the string to the specified address
Input:          char stringToWrite [], unsigned int address
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void LCDWriteString(char stringToWrite [], unsigned int address)
{
    int i;
    SelectRegister(COMMAND);
    WriteToPorts(address);
    PulseEnable();
    for (i = 0; i < strlen(stringToWrite); i++)
    {
        LCDputchar(stringToWrite[i]);
    }
}

/******************************************
Function:       WriteScore(int p1_score, int p2_score)
Description:    Writes Player's score in the appropriate address
Input:          int score
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void WriteScore(int p1_score, int p2_score)
{
    if (GameType == COOP)
    {
        char print[3];
        sprintf(print, "%d", (p1_score + p2_score));
        if ((p1_score + p2_score) < 10)
        {
            char new_print[] = "0";
            strcat(new_print, print);
            LCDWriteString(new_print, COOP_SCORE_ADDRESS);
        }
        else
        {
            LCDWriteString(print, COOP_SCORE_ADDRESS);
        }
    } 
    else
    {
        WritePlayerOneScore(p1_score);
        WritePlayerTwoScore(p2_score);
    }
}

/******************************************
Function:       WritePlayerOneScore(int score)
Description:    Writes Player One's score in the appropriate address
Input:          int score
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void WritePlayerOneScore(int score)
{
    char print[3];
    sprintf(print, "%d", score);
    if (score < 10)
    {
      char new_print[] = "0";
      strcat(new_print, print);
      LCDWriteString(new_print, PLAYER_ONE_SCORE_ADDRESS);
    }
    else
    {
      LCDWriteString(print, PLAYER_ONE_SCORE_ADDRESS);
    }
}

/******************************************
Function:       WritePlayerTwoScore(int score)
Description:    Writes Player Two's score in the appropriate address
Input:          int score
Output:         None
Revised:        PY          11/14/07
*********************************************/ 
void WritePlayerTwoScore(int score)
{
    char print[3];
    sprintf(print, "%d", score);
    if (score < 10)
    {
      char new_print[] = "0";
      strcat(new_print, print);
      LCDWriteString(new_print, PLAYER_TWO_SCORE_ADDRESS);
    }
    else
    {
      LCDWriteString(print, PLAYER_TWO_SCORE_ADDRESS);
    }
}

/******************************************
Function:       WriteTime(int time)
Description:    Writes the current time to the appropriate address
Input:          int time
Output:         None
Revised:        PY          11/14/07
*********************************************/
void WriteTime(int time)
{
    char print[3];
    sprintf(print, "%d", time);
    
    if (time < 10)
    {
      char new_print[] = " 0";
      strcat(new_print, print);
      strcat(new_print, " ");
      LCDWriteString(new_print, TIMER_ADDRESS);
    }
    else
    {
      char new_print[] = " ";
      strcat(new_print, print);
      strcat(new_print, " ");
      LCDWriteString(new_print, TIMER_ADDRESS);
    }
}

/******************************************
Function:       LCDGameSetUp(void)
Description:    Sets up the LCD screen for starting the game
Input:          None
Output:         None
Revised:        PY          11/14/07
*********************************************/
void LCDGameSetUp(void)
{
    GameType = PTAD & BIT7HI;

    printf("\r\n Game Type is %d \r\n", GameType);

    SelectRegister(COMMAND);
    WriteToPorts(0x38);
    PulseEnable();
    SelectRegister(COMMAND);    
    WriteToPorts(0x01);
    PulseEnable();
    if (GameType != VERSUS)
    {
        LCDWriteString("SCORE:", COOP_ADDRESS);
    } 
    else
    {
        LCDWriteString("PLYR 1:", PLAYER_ONE_ADDRESS);
        LCDWriteString("PLYR 2:", PLAYER_TWO_ADDRESS); 
    }
}

/******************************************
Function:       LCDGameEnd(int player1_score, int player2_score)
Description:    Prints out the correct score
Input:          None
Output:         None
Revised:        PY          11/14/07
*********************************************/
void LCDGameEnd(int player1_score, int player2_score, int win_threshold )
{
    if (GameType != VERSUS)
    {
        LCDWriteString("GAME OVER", WINNER_ADDRESS);   
    } 
    else
    {
        if (player1_score > player2_score)
        {
            LCDWriteString("PLYR 1 WINS", WINNER_ADDRESS);    
        }
        else if (player1_score < player2_score)
        {
            LCDWriteString("PLYR 2 WINS", WINNER_ADDRESS);
        }
        else if (player1_score > win_threshold)
        {
            LCDWriteString("BOTH WIN", WINNER_ADDRESS);
        }
        else 
        {
            LCDWriteString("BOTH LOSE", WINNER_ADDRESS);
        }
    }
}

/*********************************************
Function:     LCDResetGame(void)
Description:  Prints out "Insert Coin"
Input:        None
Output:       None
Revised:      CH        11/16/07
*********************************************/
void LCDResetGame(void)
{
    SelectRegister(COMMAND);
    WriteToPorts(0x38);
    PulseEnable();
       SelectRegister(COMMAND);
       WriteToPorts(0x01);
       PulseEnable();
  		 LCDWriteString("INSERT COIN", INSERTCOIN_ADDRESS);
}

/*********************************************
Function:     LCDWaitTrigger(void)
Description:  Prints out "Pull Trigger to begin"
Input:        None
Output:       None
Revised:      CH        11/16/07
*********************************************/
void LCDWaitTrigger(void)
{
    SelectRegister(COMMAND);
    WriteToPorts(0x38);
    PulseEnable();
    SelectRegister(COMMAND);    
    WriteToPorts(0x01);
    PulseEnable();
  	LCDWriteString("PULL TRIGGER", TRIGGER_ADDRESS_TOP);
  	LCDWriteString("TO BEGIN", TRIGGER_ADDRESS_BOTTOM);
}