#include <msp430x14x.h>
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "eventq.h"
#include "gpio.h"
#include "cmdparse.h"
#include "usart0.h"

static char rx0Queue[CONFIG_USART0_RX_QUEUE_SIZE];
static int rx0Head;
static int rx0Tail;
static int rx0Len;
static int rx0Errors;
static int rx0CharsDropped;

static char tx0Queue[CONFIG_USART0_TX_QUEUE_SIZE];
static int tx0Head;
static int tx0Tail;
static int tx0Len;
static int tx0CharsDropped;
static boolean tx0IntEnable;

int usart0Init(boolean spiMode)
{
  int i;

  for(i=0;i<CONFIG_USART0_RX_QUEUE_SIZE;i++)
    rx0Queue[i] = 0x00;
    
  for(i=0;i<CONFIG_USART0_TX_QUEUE_SIZE;i++)
    tx0Queue[i] = 0x00;
    
  rx0Head = 0;
  rx0Tail = 0;
  rx0Len = 0;
  rx0Errors = 0;
  rx0CharsDropped = 0;

  tx0Head = 0;
  tx0Tail = 0;
  tx0Len = 0;
  tx0CharsDropped = 0;
  tx0IntEnable = false;

  UCTL0 = SWRST; // assert reset
  UBR00 = 0x00;                         // 3.6864Mhz/9600 - 384 or 0x180
  UBR10 = 0x03;                         //
  // UBR00 = 0x80;                         // 3.6864Mhz/9600 - 384 or 0x180
  // UBR10 = 0x01;                         //
  UMCTL0 = 0x00;                        // no modulation
  UTCTL0 = SSEL1;                       // UCLK = SMCLK (0x20)
  ME1 = ME1 | (UTXE0 | URXE0);          // Enable USART0 TXD/RXD
  if (spiMode == false) {
    U0CTL = CHAR;                       // negate reset / 8-bit chars
    }
  else {
    U0CTL = (CHAR | SYNC);              // negate reset / 8-bit chars
    }
  IE1 = IE1 | URXIE0;                   // Enable USART0 RX interrupts
  _EINT();

  return 0;
}

int usart0TxOne(char txCh)
{
  int retVal = -1;
  
  if (tx0Len != CONFIG_USART0_TX_QUEUE_SIZE) {
    tx0Queue[tx0Head] = txCh;
    circInc(tx0Head, 0, (CONFIG_USART0_TX_QUEUE_SIZE-1));
    _DINT();
    tx0Len++;
    _EINT();
    retVal = 0;
    }
  else {
    tx0CharsDropped++;
    }

  if (tx0IntEnable == 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 ((UTCTL0 & TXEPT) == 0); // wait for TXBUF to become available
    TXBUF_0 = tx0Queue[tx0Tail];
    circInc(tx0Tail, 0, (CONFIG_USART0_TX_QUEUE_SIZE-1));
    _DINT();
    tx0Len--;
    tx0IntEnable = true;      
    IE1 = IE1 | UTXIE0;
    _EINT();
    }
    
  return retVal;
}

int usart0RxOne(char *pRxCh)
{
  if (rx0Len == 0)
    return -1;
    
  *pRxCh = rx0Queue[rx0Tail];
  circInc(rx0Tail, 0, (CONFIG_USART0_RX_QUEUE_SIZE-1));
  _DINT();
  rx0Len--;
  _EINT();
  
  return 0;
}

#if __VER__ < 200
interrupt [ UART0TX_VECTOR ] void usart0TxIsr( void )
#else
#pragma vector=UART0TX_VECTOR
__interrupt void usart0TxIsr( void )
#endif
{
  if (tx0Len != 0) {
    // check to see if last byte has been sent
    // while((IFG1 & UTXIFG0) == 0); // wait for TXBUF to become ready
    TXBUF_0 = tx0Queue[tx0Tail];
    circInc(tx0Tail, 0, (CONFIG_USART0_TX_QUEUE_SIZE-1));
    tx0Len--;
    }
  else {
    // disable tx empty irq
    IE1 = IE1 & ~UTXIE0;
    tx0IntEnable = false;
    }
  _EINT();
}

boolean usart0GetTxIntEnable(void)
{
  return tx0IntEnable;
}

#if __VER__ < 200
interrupt [ UART0RX_VECTOR ] void usart0RxIsr( void )
#else
#pragma vector=UART0RX_VECTOR
__interrupt void usart0RxIsr( void )
#endif
{
  char dummy;
  
  if (rx0Len == CONFIG_USART0_RX_QUEUE_SIZE)
    rx0CharsDropped++;
  else {
    if (U0RCTL & RXERR) {
      rx0Errors++; // TODO: parse U0RCTL upper nibble for detailed stats
      dummy = RXBUF_0; // read rxbuf to reset error flags
      }
    else {
      rx0Queue[rx0Head] = RXBUF_0;
      circInc(rx0Head, 0, (CONFIG_USART0_RX_QUEUE_SIZE-1));
      rx0Len++;
    }
  }
  eventqPut(EVENTQ_EVENT_UART0RX);
  _EINT();
}

static void __low_level_put(int c)
{
  int foo;
  foo = usart0TxOne((char)c);
}

int putchraw(int c)
{
  return usart0TxOne((char)c);
}

int putchar(int c)
{
  if (c == '\n')                /* Convert EOL to CR/LF */
    __low_level_put('\r');
  __low_level_put(c);
  return c;
}

