#include <msp430.h>
#include <msp430xgeneric.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include "strutils.h"
#include "mw_main.h"

#include "mw_uart.h"
#include "mw_bt.h"
#include "bt_hci.h"
#include "bt_l2cap.h"

#include "bluetooth_init_cc256x.h"


static char bt_rx_buf[BT_RX_MAX_SIZE];
static unsigned char bt_rx_buf_wpos = 0;
static unsigned char bt_rx_buf_rpos = 0;
static uint8_t mw_bt_enabled = 0;

int mw_bt_get_rxbuf_len(void)
{
	if (bt_rx_buf_rpos > bt_rx_buf_wpos)
		return (BT_RX_MAX_SIZE - bt_rx_buf_rpos) + bt_rx_buf_wpos;
	else
		return bt_rx_buf_wpos - bt_rx_buf_rpos;
}

const unsigned char *mw_bt_get_rx_buf(unsigned char **rpos, unsigned char *len)
{
	*rpos = &bt_rx_buf_rpos;

	if (bt_rx_buf_rpos > bt_rx_buf_wpos)
		*len = (BT_RX_MAX_SIZE - bt_rx_buf_rpos) + bt_rx_buf_wpos;
	else
		*len = bt_rx_buf_wpos - bt_rx_buf_rpos;

	// if we reach high water mark raise RTS to stop more data
	if (*len > (BT_RX_MAX_SIZE-(BT_RX_MAX_SIZE/10))) {
		debug_uart_tx("BT UART RTS\n");
		BT_IO_POUT |= BT_IO_RTS; // low == ready, high == !ready
	} else {
		BT_IO_POUT &= ~BT_IO_RTS; // low == ready, high == !ready
	}

	return (unsigned char *)bt_rx_buf;
}

#pragma vector=USCI_A1_VECTOR
__interrupt void UCA1_ISR (void)
{
	switch (UCA1IV) {
		case 2: // RXIFG
			/* clear IRQ flag */
			//UCA1IFG  &= ~UCRXIFG;
			/* wake up to handle the received char */
			if (UCA1STAT & UCRXERR) {
				debug_uart_tx("BT UART RXERR: ");
				if (UCA1STAT & UCOE)
					debug_uart_tx("overrun ");
				if (UCA1STAT & UCPE)
					debug_uart_tx("parity err ");
				if (UCA1STAT & UCFE)
					debug_uart_tx("frm-err ");
				debug_uart_tx("\n");
			}
			bt_rx_buf[bt_rx_buf_wpos++] = UCA1RXBUF;
			bt_rx_buf_wpos %= BT_RX_MAX_SIZE;
			// LPM3_EXIT;
			LPM3_EXIT_ISR();
			_event_src |= BT_UART_RCV_EVENT;
			break;
		case 4: // TXIFG
			debug_uart_tx("BT UART TX IRQ - huh?\n");
			break;
		default:
			break;
	};
}

void mw_init_bt_uart(const bt_uart_baud_t baud)
{
	UCA1CTL1 = UCSWRST;

	UCA1CTL1 |= UCSSEL__SMCLK;

	switch (baud) {
		case BT_UART_BD115200:
		default:
			UCA1BR0 = 138;
			UCA1MCTL = UCBRS_7 + UCBRF_0;
		break;
	};
	UCA1STAT = 0;
	// UCA1CTL0 = UCMODE_0;	// UART mode
	// UCA1CTL0 &= ~UC7BIT;	// 8bit char
	//UCA1CTL0 |= UCRXEIE;

	UCA1CTL1 &= ~UCSWRST;

	/* clear interrup flags */
	UCA1IFG = 0;
	UCA1IE = UCRXIE;
}

#if 0 // Does never finish, presumably trigger does not trigger, unknown :(
void mw_bt_uart_tx(const void *buf, const unsigned int len)
{
	UCA1IE &= UCTXIE;

	DMACTL0 = DMA0TSEL_21;

	DMA0DA  = (unsigned int) &UCA1TXBUF;
	DMA0SA  = (uint32_t) buf;
	DMA0SZ  = len;

	//DMA0CTL = 0x03F0;
	DMA0CTL = DMADT_1 | DMASRCINCR_3 | DMASBDB | DMALEVEL | DMAIE;
	UCA1IFG &= ~UCTXIFG;
	DMA0CTL |= DMAEN;

	while ((DMA0CTL & DMAIFG) == 0 && (DMA0CTL & DMAABORT) == 0)
		nop();
}
#else
int mw_bt_uart_tx(const void *buf, const unsigned int len)
{
	unsigned int pos, i;
	// char txstr[8];

	pos = 0;
	// debug_uart_tx("BT tx: ");
	while (pos < len) {
		// watch for CTS to be low
		i = 0;
		while ((BT_IO_PIN & BT_IO_CTS) && (i < 1000)) {
			__delay_cycles(16000);
			i++;
			if (i >= 1000)
				return -1;
			// nop();
		}

		// do not start a transfer if UART is busy, e.g. rx-ing
		while (UCA1STAT & UCBUSY)
			nop();

		UCA1TXBUF = *(unsigned char *) (buf+pos);
		// debug_uart_tx_char(*(unsigned char *) (buf+pos));
		// debug_uart_tx("0x");
		// hexnum2str(*(unsigned char *) (buf+pos),txstr,2);
		// debug_uart_tx(txstr);
		// debug_uart_tx(" ");
		pos++;
		while ((UCA1IFG & UCTXIFG) == 0)
			nop();
	}
	while (UCA1STAT & UCBUSY)
		nop();

	return len;
}
#endif

static int load_cc256x_init_script(void)
{
	uint32_t pos;
	unsigned char *tptr;
	int tlen;

	pos = 0;
	while (pos < cc256x_init_script_size) {
		if (_event_src != 0)
			handle_event();
		tptr = (unsigned char *)(cc256x_init_script + pos);
		tlen = mw_bt_uart_tx(tptr, 4 + tptr[3]);
		if (tlen < 0)
			return -1;
		pos += tlen /*4 + tptr[3]*/;
		// each init script part is one HCI command so wait for reply
		if (_event_src != 0)
			handle_event();
	}
	return 0;
}

void mw_enable_bt(void)
{
	int i;

	/* make sure it resets */
	BT_SHUTDOWN();
	__delay_cycles(16000);

	/* enable 32kHz ACLK output to BT module */
	P11DIR |= BIT0;
	P11SEL |= BIT0;

	// wait for clock to stabilize
	__delay_cycles(16000);

	// disable the IRQ on CTS, later used to get a wakeup IRQ from eHCILL
	// will be enabled when going to sleep
	P1IE &= ~BT_IO_CTS;
	P1IES &= ~BT_IO_CTS;

	BT_IO_PDIR &= ~(BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ);
	BT_IO_PDIR |= BT_IO_RTS;

	BT_IO_POUT &= ~(BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ);
	BT_IO_POUT &= ~BT_IO_RTS; // low == ready, high == !ready

	BT_IO_REN |= BT_IO_CTS; // enable pull-down on CTS, POUT-CTS is 0 already

	/* setup UART pins */
	BT_UART_PSEL |= BT_UART_TX_PIN | BT_UART_RX_PIN;
	// P5OUT |= BT_UART_TX_PIN | BT_UART_RX_PIN;
	// P5REN |= BT_UART_TX_PIN | BT_UART_RX_PIN;

	mw_init_bt_uart(BT_UART_BD115200);

	bt_rx_buf_wpos = 0;
	bt_rx_buf_rpos = 0;

	/* release BT reset pin */
	BT_ENABLE();

	for (i=0; i<1000; i++) {
		__delay_cycles(16000);
		if ((BT_IO_PIN & BT_IO_CTS) == 0) // when CTS goes low module is ready
			break;
	}
	if (i>=1000) {
		debug_uart_tx("Timeout waiting for CC256x to lower CTS\n");
		mw_bt_enabled = 0;
	} else {
		debug_uart_tx("CC256x CTS low - uploading init\n");

		// the init script consists of HCI cmds so HCI must be setup before
		bt_hci_init();

		// give it some more time before anyone sends data
 		for (i=0; i<10; i++) {
			__delay_cycles(16000);
		}
		if (load_cc256x_init_script() < 0) {
			debug_uart_tx("init upload failed!\n");
			return;
		}

		__delay_cycles(32000);

		debug_uart_tx("init uploaded\n");

		init_l2cap();

		if (_event_src != 0)
			handle_event();

		mw_bt_enabled = 1;
	}
}

void mw_disable_bt(void)
{
	mw_bt_enabled = 0;

	// disable the IRQ on CTS
	P1IE &= ~BT_IO_CTS;
	P1IES &= ~BT_IO_CTS;
	// BT_IO_REN &= ~BT_IO_CTS; // disable pull-down on CTS
	P1IFG &= ~BT_IO_CTS;

	/* disable UART RX interrupt */
	UCA1IE &= ~UCRXIE;

	/* disable UART pins */
	BT_UART_PSEL &= ~(BT_UART_TX_PIN | BT_UART_RX_PIN);

	/* set BT reset pin */
	BT_SHUTDOWN();

	/* disable 32kHz ACLK output to BT module */
	P11DIR &= ~BIT0;
	P11SEL &= ~BIT0;

	/* make all I/O Pins inputs so we do not drive against a "deaf" module */
	BT_IO_PDIR &= ~(BT_IO_RTS | BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ);
	BT_IO_POUT &= ~(BT_IO_RTS | BT_IO_CTS | BT_IO_PIN1 | BT_IO_PIN2 | BT_IO_CLKREQ);
}

uint8_t mw_bt_is_enabled(void)
{
	return mw_bt_enabled;
}