#include <msp430x14x.h>
#include <stdio.h>

#include "basic.h"
#include "config.h"
#include "eventq.h"
#include "gpio.h"
#include "cmdparse.h"
#include "usart.h"

//
// Generic Rx and Tx queue structures
//

struct RxQueue_tag {
  char * rxQueue;
  int rxHead;
  int rxTail;
  int rxLen;
  int rxSize;
  int rxErrors;
  int rxCharsDropped;
  } RxQueue;
  
struct TxQueue_tag {
  char * txQueue;
  int txHead;
  int txTail;
  int txLen;
  int txSize;
  int txCharsDropped;
  boolean txIntEnable;
  } TxQueue;

//
// USART0/1 Rx and Tx queues
//

static char rx0Queue[CONFIG_USART0_RX_QUEUE_SIZE];
static char tx0Queue[CONFIG_USART0_TX_QUEUE_SIZE];

#ifdef CONFIG_SIDEARM_HW_REV4
static char rx1Queue[CONFIG_USART1_RX_QUEUE_SIZE];
static char tx1Queue[CONFIG_USART0_TX_QUEUE_SIZE];
#endif

#ifdef CONFIG_SIDEARM_HW_REV4
static RxQueue rx[2] = { {rx0Queue, 0, 0, 0, CONFIG_USART0_RX_QUEUE_SIZE, 0, 0 }, 
                         {rx1Queue, 0, 0, 0, CONFIG_USART1_RX_QUEUE_SIZE, 0, 0 } };

static TxQueue tx[2] = { {tx0Queue, 0, 0, 0, CONFIG_USART0_TX_QUEUE_SIZE, 0, 0 }, 
                         {tx1Queue, 0, 0, 0, CONFIG_USART1_TX_QUEUE_SIZE, 0, 0 } };
#endif

#ifdef CONFIG_SIDEARM_HW_REV3
static RxQueue rx[2] = { {rx0Queue, 0, 0, 0, CONFIG_USART0_RX_QUEUE_SIZE, 0, 0 }, 
                         {0, 0, 0, 0, 0, 0, 0 } };

static TxQueue tx[2] = { {tx0Queue, 0, 0, 0, CONFIG_USART0_TX_QUEUE_SIZE, 0, 0 }, 
                         {0, 0, 0, 0, 0, 0, 0 } };
#endif


int showUsartHandler(char *cmdStr) 
{
  //printf("rxErrors[%d] rxCharsDropped[%d]\n",rxErrors,rxCharsDropped);
  //printf("txCharsDropped[%d]\n",txCharsDropped);
  return 0;
}

static INLINE boolean validateChannel(int chNum)
{
  if ((chNum == 0) || (chNum == 1))
    return true;
  else
    return false;
}

int usartInit(void)
{
  int i, j;
  int size;
  char * pCh;
  
  // initialize Rx for both channels
  for (j=0;j<1;j++) {
    pCh = rx[j].rxQueue;
    size = rx[j].rxSize;
    for(i=0;i<size;i++) {
      pCh[i] = 0x00;
      }
  }

  // initialize Tx for both channels   
  for (j=0;j<1;j++) {
    pCh = tx[j].txQueue;
    size = tx[j].txSize;
    for(i=0;i<size;i++) {
      pCh[i] = 0x00;
      }
  }

  UCTL0 = SWRST; // assert reset
#ifdef CONFIG_SIDEARM_HW_REV4
  UBR00 = 0x00;                         // 7.37Mhz/9600 = 0x300 (1x sampled?)
  UBR10 = 0x03;                         //
#else    
  UBR00 = 0x80;                         // 3.6864Mhz/9600 - 384 or 0x180
  UBR10 = 0x01;                         //
#endif
  UMCTL0 = 0x00;                        // no modulation
  UTCTL0 = SSEL1;                       // UCLK = SMCLK (0x20)
  ME1 = ME1 | (UTXE0 | URXE0);          // Enable USART0 TXD/RXD
  UCTL0 = CHAR;                         // negate reset / 8-bit chars
  IE1 = IE1 | URXIE0;                   // Enable USART0 RX interrupts

#ifdef CONFIG_SIDEARM_HW_REV4
  UCTL1 = SWRST; // assert reset
  UBR01 = 0x00;                         // 7.37Mhz/9600 = 0x300 (1x sampled?)
  UBR11 = 0x03;                         //
  UMCTL1 = 0x00;                        // no modulation
  UTCTL1 = SSEL1;                       // UCLK = SMCLK (0x20)
  ME1 = ME1 | (UTXE1 | URXE1);          // Enable USART1 TXD/RXD
  UCTL1 = CHAR;                         // negate reset / 8-bit chars
  IE1 = IE1 | URXIE1;                   // Enable USART1 RX interrupts
#endif

  _EINT();

  cmdparseAddCommand("show","usart",showUsartHandler);

  return 0;
}

int usartTxOne(int channel, char txCh)
{
  int retVal = -1;
  
  if (validateChannel(channel) != true)
    return retVal;

  TxQueue *pTx = tx[channel];

  if (pTx->txLen != pTx->txSize) {
    pTx->txQueue[pTx->txHead] = txCh;
    circInc(pTx->txHead, 0, (pTx->txSize-1));
    _DINT();
    (pTx->txLen)++;
    _EINT();
    retVal = 0;
    }
  else {
    (pTx->txCharsDropped)++;
    }

  if (pTx->txIntEnable == 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 = txQueue[txTail];
    circInc(txTail, 0, (TX_QUEUE_SIZE-1));
    _DINT();
    txLen--;
    txIntEnable = true;      
    IE1 = IE1 | UTXIE0;
    _EINT();
    }
    
  return retVal;
}

int usartRxOne(char *pRxCh)
{
  if (rxLen == 0)
    return -1;
    
  *pRxCh = rxQueue[rxTail];
  circInc(rxTail, 0, (RX_QUEUE_SIZE-1));
  _DINT();
  rxLen--;
  _EINT();
  
  return 0;
}

#if __VER__ < 200
interrupt [ UART0TX_VECTOR ] void usartTxIsr( void )
#else
#pragma vector=UART0TX_VECTOR
__interrupt void usartTxIsr( void )
#endif
{
  if (txLen != 0) {
    // check to see if last byte has been sent
    // while((IFG1 & UTXIFG0) == 0); // wait for TXBUF to become ready
    TXBUF_0 = txQueue[txTail];
    circInc(txTail, 0, (TX_QUEUE_SIZE-1));
    txLen--;
    }
  else {
    // disable tx empty irq
    IE1 = IE1 & ~UTXIE0;
    txIntEnable = false;
    }
  _EINT();
}

boolean usartGetTxIntEnable(void)
{
  return txIntEnable;
}

#if __VER__ < 200
interrupt [ UART0RX_VECTOR ] void usartRxIsr( void )
#else
#pragma vector=UART0RX_VECTOR
__interrupt void usartRxIsr( void )
#endif
{
  char dummy;
  
  if (rxLen == RX_QUEUE_SIZE)
    rxCharsDropped++;
  else {
    if (U0RCTL & RXERR) {
      rxErrors++; // TODO: parse U0RCTL upper nibble for detailed stats
      dummy = RXBUF_0; // read rxbuf to reset error flags
      }
    else {
      rxQueue[rxHead] = RXBUF_0;
      circInc(rxHead, 0, (RX_QUEUE_SIZE-1));
      rxLen++;
    }
  }
  eventqPut(EVENTQ_EVENT_UART0RX);
  _EINT();

}



static void __low_level_put(int c)
{
  int foo;
  foo = usartTxOne((char)c);
}

int putchraw(int c)
{
  return usartTxOne((char)c);
}

int putchar(int c)
{
  if (c == '\n')                /* Convert EOL to CR/LF */
    __low_level_put('\r');
  __low_level_put(c);
  return c;
}
