#include <msp430x14x.h>
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "eventq.h"
#include "gpio.h"
#include "cmdparse.h"
#include "usart1.h"

static char rx1Queue[CONFIG_USART1_RX_QUEUE_SIZE];
static int rx1Head;
static int rx1Tail;
static int rx1Len;
static int rx1Errors;
static int rx1CharsDropped;

static char tx1Queue[CONFIG_USART1_TX_QUEUE_SIZE];
static int tx1Head;
static int tx1Tail;
static int tx1Len;
static int tx1CharsDropped;
static boolean tx1IntEnable;

int usart1Init(boolean spiMode)
{
  int i;

  for(i=0;i<CONFIG_USART1_RX_QUEUE_SIZE;i++)
    rx1Queue[i] = 0x00;
    
  for(i=0;i<CONFIG_USART1_TX_QUEUE_SIZE;i++)
    tx1Queue[i] = 0x00;

  rx1Head = 0;
  rx1Tail = 0;
  rx1Len = 0;
  rx1Errors = 0;
  rx1CharsDropped = 0;

  tx1Head = 0;
  tx1Tail = 0;
  tx1Len = 0;
  tx1CharsDropped = 0;
  tx1IntEnable = false;

  U1CTL = SWRST; // assert reset
  U1BR0 = 0x00;                         // 4800 baud @ 7.37 MHz
  U1BR1 = 0x06;                         //
  U1MCTL = 0x00;                        // no modulation
  U1TCTL = SSEL1;                       // UCLK = SMCLK (0x20)
  ME2 = ME2 | (UTXE1 | URXE1);          // Enable USART1 TXD/RXD in ME2
  if (spiMode == false) {
    U1CTL = CHAR;                       // negate reset / 8-bit chars
    }
  else {
    U1CTL = (CHAR | SYNC);              // negate reset / 8-bit chars
    }    
  IE2 = IE2 | URXIE1;                   // Enable USART1 RX interrupts
  _EINT();

  return 0;
}

int usart1TxOne(char txCh)
{
  int retVal = -1;
  
  if (tx1Len != CONFIG_USART1_TX_QUEUE_SIZE) {
    tx1Queue[tx1Head] = txCh;
    circInc(tx1Head, 0, (CONFIG_USART1_TX_QUEUE_SIZE-1));
    _DINT();
    tx1Len++;
    _EINT();
    retVal = 0;
    }
  else {
    tx1CharsDropped++;
    }

  if (tx1IntEnable == false) {
    // 
    // interrupts are not enabled, so the transmitter is idle. Prime the
    // pump by sending the first character right away and enabling the
    // interrupt for subsequent characters. If there are no more chars
    // to be sent, the ISR will turn off the transmitter available 
    // interrupt, otherwise the FIFO will be drained by the ISR.
    //
    while ((UTCTL1 & TXEPT) == 0); // wait for TXBUF to become available
    U1TXBUF = tx1Queue[tx1Tail];
    circInc(tx1Tail, 0, (CONFIG_USART1_TX_QUEUE_SIZE-1));
    _DINT();
    tx1Len--;
    tx1IntEnable = true;      
    IE2 = IE2 | UTXIE1;
    _EINT();
    }
    
  return retVal;
}

int usart1RxOne(char *pRxCh)
{
  if (rx1Len == 0)
    return -1;
    
  *pRxCh = rx1Queue[rx1Tail];
  circInc(rx1Tail, 0, (CONFIG_USART1_RX_QUEUE_SIZE-1));
  _DINT();
  rx1Len--;
  _EINT();
  
  return 0;
}

#if __VER__ < 200
interrupt [ UART1TX_VECTOR ] void usart1TxIsr( void )
#else
#pragma vector=UART1TX_VECTOR
__interrupt void usart1TxIsr( void )
#endif
{
  if (tx1Len != 0) {
    // check to see if last byte has been sent
    // while((IFG1 & UTXIFG0) == 0); // wait for TXBUF to become ready
    U1TXBUF = tx1Queue[tx1Tail];
    circInc(tx1Tail, 0, (CONFIG_USART1_TX_QUEUE_SIZE-1));
    tx1Len--;
    }
  else {
    // disable tx empty irq
    IE2 = IE2 & ~UTXIE1;
    tx1IntEnable = false;
    }
  _EINT();
}

boolean usart1GetTxIntEnable(void)
{
  return tx1IntEnable;
}

#if __VER__ < 200
interrupt [ UART1RX_VECTOR ] void usart1RxIsr( void )
#else
#pragma vector=UART0RX_VECTOR
__interrupt void usart1RxIsr( void )
#endif
{
  char dummy;
  
  if (rx1Len == CONFIG_USART1_RX_QUEUE_SIZE)
    rx1CharsDropped++;
  else {
    if (U1RCTL & RXERR) {
      rx1Errors++; // TODO: parse U0RCTL upper nibble for detailed stats
      dummy = U1RXBUF; // read rxbuf to reset error flags
      }
    else {
      rx1Queue[rx1Head] = U1RXBUF;
      circInc(rx1Head, 0, (CONFIG_USART1_RX_QUEUE_SIZE-1));
      rx1Len++;
    }
  }
  eventqPut(EVENTQ_EVENT_UART1RX);
  _EINT();

}

