#include <mega16.h>
#include <alcd.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>

#define LED             PORTC
#define LED_DDR         DDRC

flash const char CMD_LED[]  = "LED";
flash const char CMD_ALCD[] = "ALCD";

#define DATA_REGISTER_EMPTY (1<<UDRE)
#define RX_COMPLETE (1<<RXC)
#define FRAMING_ERROR (1<<FE)
#define PARITY_ERROR (1<<UPE)
#define DATA_OVERRUN (1<<DOR)

// USART Receiver buffer
#define RX_BUFFER_SIZE 64
char rxBuffer[RX_BUFFER_SIZE];

uint8_t rxBufferIndex = 0;
uint8_t rxBufferOverflow = 0;
uint8_t rxLineFlag = 0;

// USART Receiver interrupt service routine
interrupt [USART_RXC] void usart_rx_isr(void)
{
char status,data;
status=UCSRA;
data=UDR;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
   {
      rxBuffer[rxBufferIndex] = data;
      
      if (rxBufferIndex != 0 && 
          data == '\n' &&
          rxBuffer[rxBufferIndex - 1] == '\r') {
          
          rxLineFlag = 1;
      }
      
      if (++rxBufferIndex == RX_BUFFER_SIZE) {
        rxBufferOverflow = 1;
        rxBufferIndex = 0;
      }
   }
}


/**
 * int x = 10;
 * -------------
 * Name: x
 * Type: int
 * Value: 10
 * Address: &x 
 * -------------
 * x = 20;
 * -------------
 * int* pX = &x;
 * *pX = 20;
 **/



void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port A initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRA=(0<<DDA7) | (0<<DDA6) | (0<<DDA5) | (0<<DDA4) | (0<<DDA3) | (0<<DDA2) | (0<<DDA1) | (0<<DDA0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTA=(0<<PORTA7) | (0<<PORTA6) | (0<<PORTA5) | (0<<PORTA4) | (0<<PORTA3) | (0<<PORTA2) | (0<<PORTA1) | (0<<PORTA0);

// Port B initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRB=(0<<DDB7) | (0<<DDB6) | (0<<DDB5) | (0<<DDB4) | (0<<DDB3) | (0<<DDB2) | (0<<DDB1) | (0<<DDB0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTB=(0<<PORTB7) | (0<<PORTB6) | (0<<PORTB5) | (0<<PORTB4) | (0<<PORTB3) | (0<<PORTB2) | (0<<PORTB1) | (0<<PORTB0);

// Port C initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRC=(0<<DDC7) | (0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (0<<DDC3) | (0<<DDC2) | (0<<DDC1) | (0<<DDC0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTC=(0<<PORTC7) | (0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (0<<PORTC3) | (0<<PORTC2) | (0<<PORTC1) | (0<<PORTC0);

// Port D initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=In Bit1=In Bit0=In 
DDRD=(0<<DDD7) | (0<<DDD6) | (0<<DDD5) | (0<<DDD4) | (0<<DDD3) | (0<<DDD2) | (0<<DDD1) | (0<<DDD0);
// State: Bit7=T Bit6=T Bit5=T Bit4=T Bit3=T Bit2=T Bit1=T Bit0=T 
PORTD=(0<<PORTD7) | (0<<PORTD6) | (0<<PORTD5) | (0<<PORTD4) | (0<<PORTD3) | (0<<PORTD2) | (0<<PORTD1) | (0<<PORTD0);

// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: On
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=(0<<RXC) | (0<<TXC) | (0<<UDRE) | (0<<FE) | (0<<DOR) | (0<<UPE) | (0<<U2X) | (0<<MPCM);
UCSRB=(1<<RXCIE) | (0<<TXCIE) | (0<<UDRIE) | (1<<RXEN) | (1<<TXEN) | (0<<UCSZ2) | (0<<RXB8) | (0<<TXB8);
UCSRC=(1<<URSEL) | (0<<UMSEL) | (0<<UPM1) | (0<<UPM0) | (0<<USBS) | (1<<UCSZ1) | (1<<UCSZ0) | (0<<UCPOL);
UBRRH=0x00;
UBRRL=0x33;

// Alphanumeric LCD initialization
// Connections are specified in the
// Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu:
// RS - PORTA Bit 0
// RD - PORTA Bit 1
// EN - PORTA Bit 2
// D4 - PORTA Bit 4
// D5 - PORTA Bit 5
// D6 - PORTA Bit 6
// D7 - PORTA Bit 7
// Characters/line: 16
lcd_init(16);

LED_DDR = 0xFF;
LED = 0x00;

// Global enable interrupts
#asm("sei")

while (1)
      {
        if (rxLineFlag) {                          
          char* pDot;
          rxBuffer[rxBufferIndex - 2] = '\0';
          // find ':'
          pDot = strchr(rxBuffer, ':');
          if (pDot != NULL) {
            uint16_t len = (uint16_t) pDot - (uint16_t) rxBuffer;  
            /**
             * "Ali" < "ali" =>  strcmp("Ali", "ali") < 0
             * "ali" > "Ali" =>  strcmp("ali", "Ali") > 0
             * "123" == "123" => strcmp("123", "123") == 0
             **/     
            if (strncmpf(rxBuffer, CMD_LED, len) == 0) {
              // LED:0,1\r\n
              // LED:8,0\r\n
              // LED:2 ,3\r\n  -> Wrong
              // LED:3 , 2\r\n -> Wrong
              char* pComma;
              pComma = strchr(++pDot, ',');
              if (pComma != NULL) {
                int ledNum;
                *pComma = '\0';
                ledNum = atoi(pDot);
                // check ledNum
                if (ledNum >= 0 && ledNum <= 7) {
                  int ledState;
                  ledState = atoi(++pComma);
                  switch (ledState) {
                    case 0:
                      LED&= ~(1 << ledNum);
                      break;             
                    case 1:
                      LED |= 1 << ledNum;
                      break;              
                    default:
                      lcd_clear();
                      lcd_gotoxy(0, 1);
                      lcd_putsf("LedState Err!");
                      break;
                  }
                }
                else {
                  lcd_clear();
                  lcd_gotoxy(0, 1);
                  lcd_putsf("LedNum Err!");
                }
              }
              else {
                lcd_clear();
                lcd_gotoxy(0, 1);
                lcd_putsf("Led Cmd Failed!");
              } 
            } 
            else if (strncmpf(rxBuffer, CMD_ALCD, len) == 0) {
              // ALCD:Hello\r\n
              pDot++;  
              lcd_clear();
              lcd_gotoxy(0, 0);
              lcd_puts(pDot);
            }                                   
            else {
              lcd_clear();
              lcd_gotoxy(0, 1);
              lcd_putsf("Wrong Cmd!");
            }
          }                        
          else {
            lcd_clear();
            lcd_gotoxy(0, 1);
            lcd_putsf("Wrong Cmd Format!");
          }              
          
          rxBufferIndex = 0;
          rxLineFlag = 0;
        }
      }
}
