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

#include "mw_main.h"

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

#include "strutils.h"


static uint8_t local_bdaddr[6];

uint8_t *bt_hci_get_local_bdaddr(void)
{
	return local_bdaddr;
}

static void bt_print_bd_addr(uint8_t *bd_addr)
{
#if defined MW_DEVBOARD_V2
	int i;
	char nums[4];

	for (i=5; i>=0; i--) {
		hexnum2str(bd_addr[i],nums,4);
		debug_uart_tx(nums);
		if (i>0)
			debug_uart_tx_char(':');
	}
#endif
}

void bt_hci_process_acl_packet(unsigned char *packet)
{
#if defined MW_DEVBOARD_V2
	char tstr[32];
#endif
	uint16_t dlen;
	uint16_t handle;
	L2CAP_PB_FLAG PB;
	L2CAP_BC_FLAG BC;
	uint16_t channel;
	uint16_t mlen;

//	debug_uart_tx("ACL packet, ");
	handle = packet[0] | ((packet[1] & 0x0f) << 8);
//	debug_uart_tx("handle 0x");
//	hexnum2str(handle,tstr,4);
//	debug_uart_tx(tstr);
	PB = (packet[1] >> 4) & 0x03;
	BC = (packet[1] >> 6) & 0x03;
//	debug_uart_tx(" PB 0x");
//	hexnum2str(PB,tstr,2);
//	debug_uart_tx(tstr);
//	debug_uart_tx(" BC 0x");
//	hexnum2str(BC,tstr,2);
//	debug_uart_tx(tstr);

	dlen = packet[2] | ((uint16_t)packet[3] << 8);
//	debug_uart_tx(" len 0x");
//	hexnum2str(dlen,tstr,4);
//	debug_uart_tx(tstr);
//	debug_uart_tx("\n");
//	debug_dump_hex(dlen+4, packet);

	channel = packet[4+2] | (packet[4+3] << 8);
	mlen = packet[4] | (packet[4+1] << 8);
	switch (channel) {
		case L2CAP_CID_SIGNALING:
			bt_l2cap_proc_signalling(handle, (packet+8), mlen);
			break;
		case L2CAP_CID_CONNECTIONLESS_CHANNEL:
			break;
		case L2CAP_CID_ATTRIBUTE_PROTOCOL:
			break;
		case L2CAP_CID_SIGNALING_LE:
			break;
		case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
			break;
		default:
			if (channel > 0x3f) {
				// just for sure, add a 0 as string delimiter
				packet[mlen+8] = 0x00;
				bt_l2cap_proc_dyn_channel(channel, handle, (packet+8), mlen);
			} else {
#if defined MW_DEVBOARD_V2
				debug_uart_tx("L2CAP unhandled CID 0x");
				hexnum2str(channel,tstr,4);
				debug_uart_tx(tstr);
				debug_uart_tx("\n");
#endif
			}
			break;
	}
}

void bt_hci_process_event_packet(unsigned char *packet)
{
#if defined MW_DEVBOARD_V2
	char tstr[32];
#endif
	int i;
	// uint8_t bd_addr[6];
	uint32_t dev_type;

//	debug_uart_tx("Event packet, evt 0x");
//	hexnum2str(packet[0],tstr,2);
//	debug_uart_tx(tstr);
//	debug_uart_tx(": ");

	switch (packet[0]) {
		case HCI_EVENT_INQUIRY_COMPLETE:
			debug_uart_tx("inq complete\n");
			break;
		case HCI_EVENT_INQUIRY_RESULT:
			debug_uart_tx("inq result\n");
			break;
		case HCI_EVENT_CONNECTION_COMPLETE:
			debug_uart_tx("con complete from ");
#if defined MW_DEVBOARD_V2
			bt_print_bd_addr((packet+5));
			debug_uart_tx(" status 0x");
			hexnum2str(packet[2],tstr,2);
			debug_uart_tx(tstr);
			debug_uart_tx(" handle 0x");
			hexnum2str(packet[3],tstr,2);
			debug_uart_tx(tstr);
			debug_uart_tx(" type 0x");
			hexnum2str(packet[11],tstr,2);
			debug_uart_tx(tstr);
			debug_uart_tx(" enc 0x");
			hexnum2str(packet[12],tstr,2);
			debug_uart_tx(tstr);
			debug_uart_tx("\n");
			if (packet[2] == 0x00)
				debug_uart_tx("connection established\n");
			else
				debug_uart_tx("connection failed\n");
#endif
			break;
		case HCI_EVENT_CONNECTION_REQUEST: {
			uint8_t bd_addr[7];

			switch (packet[11]) {
				case HCI_LINK_TYPE_SCO:
					debug_uart_tx("SCO");
					break;
				case HCI_LINK_TYPE_ACL:
					debug_uart_tx("ACL");
					break;
				case HCI_LINK_TYPE_ESCO:
					debug_uart_tx("eSCO");
					break;
				default:
					debug_uart_tx("unknown type");
					break;
			}
			debug_uart_tx(" con req from ");
			for (i=0; i<6; i++) {
				bd_addr[i] = packet[i+2];
			}
			bt_print_bd_addr(bd_addr);
			dev_type = (uint32_t)packet[8] << 16;
			dev_type |= (uint32_t)packet[9] << 8;
			dev_type |= packet[10];
#if defined MW_DEVBOARD_V2
			debug_uart_tx(" rem. dtype 0x");
			hexnum2str((dev_type>>16)&0xFF,tstr,2);
			hexnum2str(dev_type&0xFFFF,tstr+2,4);
			debug_uart_tx(tstr);
			debug_uart_tx("\n");
#endif
			//memcpy(tstr, bd_addr, 6);
			//tstr[6] = 0x01; /* remain slave */
			bd_addr[6] = 0x01; /* remain slave */
			bt_hci_cmd(HCI_LINK_CTRL_OGF, HCI_ACCEPT_CONN_REQ_OCF, 7, bd_addr);
			} break;
		case HCI_EVENT_DISCONNECTION_COMPLETE:
			debug_uart_tx("discon complete\n");
			break;
		case HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT:
			debug_uart_tx("auth complete\n");
			break;
		case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
			debug_uart_tx("rem name req complete\n");
			break;
		case HCI_EVENT_ENCRYPTION_CHANGE:
			debug_uart_tx("enc change\n");
			break;
		case HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE:
			debug_uart_tx("change con link key complete\n");
			break;
		case HCI_EVENT_MASTER_LINK_KEY_COMPLETE:
			debug_uart_tx("master link key complete\n");
			break;
		case HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE:
			debug_uart_tx("read rem feat. complete\n");
			break;
		case HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE:
			debug_uart_tx("read rem version complete\n");
			break;
		case HCI_EVENT_QOS_SETUP_COMPLETE:
			debug_uart_tx("qos setup complete\n");
			break;
		case HCI_EVENT_COMMAND_COMPLETE:
			// num2str(packet[2],tstr,1,5);
			// debug_uart_tx(tstr);
			// debug_uart_tx(" cmd(s) complete: 0x");
			// hexnum2str(packet[3],str,2);
			// debug_uart_tx(tstr);
			// hexnum2str(packet[4],str,2);
			// debug_uart_tx(tstr);
			// debug_uart_tx("=");
			// num2str(packet[5],tstr,1,5);
			// debug_uart_tx(tstr);
			if (packet[2] > 0 &&
			    packet[3] == ((HCI_R_BD_ADDR_OCF | (HCI_INFO_PARAM_OGF << 10)) & 0xff) &&
			    packet[4] == (((HCI_R_BD_ADDR_OCF | (HCI_INFO_PARAM_OGF << 10)) & 0xff00) >> 8)) { // read local bdaddr
				memcpy(local_bdaddr, (packet+6), 6);
#if defined MW_DEVBOARD_V2
				debug_uart_tx("local bdaddr = ");
				bt_print_bd_addr((uint8_t *)(packet+6));
				debug_uart_tx("\n");
#endif
			}
			break;
		case HCI_EVENT_COMMAND_STATUS:
			debug_uart_tx("cmd status\n");
			break;
		case HCI_EVENT_HARDWARE_ERROR:
#if defined MW_DEVBOARD_V2
			debug_uart_tx("hardw err 0x");
			hexnum2str(packet[2],str,2);
			debug_uart_tx(tstr);
			debug_uart_tx("\n");
#endif
			break;
		case HCI_EVENT_FLUSH_OCCURED:
			debug_uart_tx("flush occured\n");
			break;
		case HCI_EVENT_ROLE_CHANGE:
			debug_uart_tx("role change\n");
			break;
		case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
			debug_uart_tx("numb compl. packets\n");
			break;
		case HCI_EVENT_MODE_CHANGE_EVENT:
			debug_uart_tx("mode change\n");
			break;
		case HCI_EVENT_RETURN_LINK_KEYS:
			debug_uart_tx("return link keys\n");
			break;
		case HCI_EVENT_PIN_CODE_REQUEST:
			debug_uart_tx("pin code request\n");
#if defined MW_DEVBOARD_V2
			debug_uart_tx("from ");
			bt_print_bd_addr((uint8_t *)(packet+2));
			debug_uart_tx("\n");
#endif
			memmove(packet, (packet+2), 6);
			packet[6] = 0x04; // PIN has length of 4
			memcpy((packet+7), BT_PIN, 4);
			packet[7] = '4';
			packet[8] = '3';
			packet[9] = '1';
			packet[10] = '2';
			bt_hci_cmd(HCI_LINK_CTRL_OGF, HCI_PIN_CODE_REQ_REP_OCF, 11, packet);
			break;
		case HCI_EVENT_LINK_KEY_REQUEST:
			debug_uart_tx("link key request\n");
			break;
		case HCI_EVENT_LINK_KEY_NOTIFICATION:
			debug_uart_tx("link key notify\n");
			break;
		case HCI_EVENT_DATA_BUFFER_OVERFLOW:
			debug_uart_tx("evt data buf overflow\n");
			break;
		case HCI_EVENT_MAX_SLOTS_CHANGED:
			debug_uart_tx("max slots changed\n");
			break;
		case HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE:
			debug_uart_tx("read clock offset compl.\n");
			break;
		case HCI_EVENT_PACKET_TYPE_CHANGED:
			debug_uart_tx("packet type changed\n");
			break;
		case HCI_EVENT_PAGE_SCAN_REPETION_MODE_CHANGE:
			debug_uart_tx("page scan repetition mode changed\n");
			break;
		case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
			debug_uart_tx("inq result with RSSI\n");
			break;
		case HCI_EVENT_EXTENDED_INQUIRY_RESPONSE:
			debug_uart_tx("ext. inq. resp.\n");
			break;
		case HCI_EVENT_LE_META:
			debug_uart_tx("LE meta\n");
			break;
		case HCI_EVENT_VENDOR_SPECIFIC:
			debug_uart_tx("vend. spec.\n");
			break;
		default:
			debug_uart_tx("unknown\n");
			break;
	}
//	debug_uart_tx("\n");
//	for (i=2; i<=(packet[1]+1); i++) {
//		debug_uart_tx(" 0x");
//		hexnum2str(packet[i],tstr,2);
//		debug_uart_tx(tstr);
//	}
//	debug_uart_tx("\n");
}

typedef enum {
	HCI_PACKET_START = 0,
	HCI_CMD_PACKET,
	HCI_ACL_PACKET_HEADER,
	HCI_ACL_PACKET_DATA,
	HCI_SCO_PACKET,
	HCI_EVENT_PACKET_HEADER,
	HCI_EVENT_PACKET_DATA,
	HCI_EHCILL_PACKET,
	EHCILL_SLEEPING
} bt_hci_state_t;

static bt_hci_state_t state = HCI_PACKET_START;

unsigned char bt_feed_packet_data(unsigned char pdata)
{
	char tstr[32];
	static unsigned char packet[255];
	static uint16_t bytes_left = 0;
	static uint16_t pdata_pos = 0;

//	debug_uart_tx("bt 0x");
//	hexnum2str(pdata,tstr,2);
//	debug_uart_tx(tstr);
//	debug_uart_tx(" ");
	switch (state) {
		case HCI_PACKET_START:
			switch (pdata) {
				case HCI_EVENT_PACKET:
					state = HCI_EVENT_PACKET_HEADER;
					bytes_left = 1;
					pdata_pos = 0;
					memset(packet, 0, 64);
					break;
				case HCI_ACL_DATA_PACKET:
					state = HCI_ACL_PACKET_HEADER;
					bytes_left = 3;
					pdata_pos = 0;
					memset(packet, 0, 64);
					break;
				case EHCILL_GO_TO_SLEEP_IND:
					debug_uart_tx("eHCILL go to sleep ind\n");
					state = HCI_PACKET_START;
					// disable BT UART?
					// mabye UCA1CTL1 = UCSWRST ?

					pdata = EHCILL_GO_TO_SLEEP_ACK;
					mw_bt_uart_tx(&pdata, 0x01);

					// pull RTS -> stop data
					BT_IO_POUT |= BT_IO_RTS;

					// enable IRQ on CTS
					P1IFG &= ~BT_IO_CTS;
					P1IE |= BT_IO_CTS;

					state = EHCILL_SLEEPING;
					break;
				case EHCILL_GO_TO_SLEEP_ACK:
					debug_uart_tx("eHCILL go to sleep ack\n");
					state = HCI_PACKET_START;
					break;
				case EHCILL_WAKE_UP_IND:
					debug_uart_tx("eHCILL wake up ind\n");
					state = HCI_PACKET_START;
					break;
				case EHCILL_WAKE_UP_ACK:
					debug_uart_tx("eHCILL wake up ack\n");
					state = HCI_PACKET_START;
					break;
				default:
					debug_uart_tx("unexpected packet start\n");
					break;
			}
			break;
		case HCI_EVENT_PACKET_HEADER:
			if (bytes_left != 0) {
				packet[pdata_pos++] = pdata;
				bytes_left--;
			} else {
				state = HCI_EVENT_PACKET_DATA;
				packet[pdata_pos++] = pdata;
				bytes_left = pdata;
			}
			break;
		case HCI_EVENT_PACKET_DATA:
			packet[pdata_pos++] = pdata;
			bytes_left--;
			if (bytes_left == 0) {
				state = HCI_PACKET_START;
				bt_hci_process_event_packet(packet);
			}
			break;
		case HCI_ACL_PACKET_HEADER:
			if (bytes_left != 0) {
				packet[pdata_pos++] = pdata;
				bytes_left--;
			} else {
				state = HCI_ACL_PACKET_DATA;
				packet[pdata_pos] = pdata;
				bytes_left = (packet[pdata_pos-1] | (packet[pdata_pos] << 8));
//				debug_uart_tx("ACL data len 0x");
//				hexnum2str(bytes_left,tstr,4);
//				debug_uart_tx(tstr);
//				debug_uart_tx("\n");
//				num2str(pdata_pos,tstr,1,5);
//                              debug_uart_tx(tstr);
//				debug_uart_tx(" (0x");
//				hexnum2str(packet[pdata_pos-1],tstr,2);
//                              debug_uart_tx(tstr);
//                              debug_uart_tx(" 0x");
//				hexnum2str(packet[pdata_pos],tstr,2);
//                              debug_uart_tx(tstr);
//				debug_uart_tx("\n");
				pdata_pos++;
			}
			break;
		case HCI_ACL_PACKET_DATA:
//			hexnum2str(pdata,tstr,2);
//			debug_uart_tx(tstr);
//			debug_uart_tx(" ");
			packet[pdata_pos++] = pdata;
			bytes_left--;
			if (bytes_left == 0) {
//				debug_uart_tx("\n");
				state = HCI_PACKET_START;
				bt_hci_process_acl_packet(packet);
			}
			break;
		default:
			debug_uart_tx("hosed HCI state!\n");
#if defined MW_DEVBOARD_V2
			debug_uart_tx("  state= ");
			num2str(state,tstr,1,5);
			debug_uart_tx(tstr);
			debug_uart_tx("\n");
#endif
			break;
	};

	// one byte consumed
	return 1;
}

typedef struct {
	uint8_t type;
	uint16_t cmd;
	uint8_t length;
} __attribute__((packed)) bt_hci_cmd_t;

void bt_hci_cmd(const uint8_t OGF, const uint8_t OCF, const uint8_t data_len, const void *data)
{
	bt_hci_cmd_t packet;

	// refuse any HCI if interface is not enabled
	if (mw_bt_is_enabled() == 0)
		return;

	if (state == EHCILL_SLEEPING) {
		uint8_t ehcill_p = EHCILL_WAKE_UP_IND;

		debug_uart_tx("wakeup HCILL\n");
		state = HCI_PACKET_START;
		mw_bt_uart_tx(&ehcill_p, 1);
		__delay_cycles(300000);
		BT_IO_POUT &= ~BT_IO_RTS; // drop RTS -> start data
	}
	packet.type = HCI_COMMAND_PACKET;
	packet.cmd = OCF | (OGF<<10);
	packet.length = data_len;
	mw_bt_uart_tx(&packet, sizeof(bt_hci_cmd_t));
	if (data_len > 0 && data != NULL)
		mw_bt_uart_tx(data, data_len);
}

typedef struct {
	uint16_t acl_handle;
	uint16_t max_interval;
	uint16_t min_interval;
	uint16_t sniff_attempt;
	uint16_t sniff_timeout;
} __attribute__((packed)) bt_hci_sniff_cmd_t;

void bt_hci_set_sniff_mode(const uint16_t acl_handle, const uint16_t max_interval, const uint16_t min_interval, const uint16_t sniff_attempt, const uint16_t sniff_timeout)
{
	bt_hci_sniff_cmd_t sniff_cmd;

	sniff_cmd.acl_handle = acl_handle;
	sniff_cmd.max_interval = max_interval;
	sniff_cmd.min_interval = min_interval;
	sniff_cmd.sniff_attempt = sniff_attempt;
	sniff_cmd.sniff_timeout = sniff_timeout;
	
	bt_hci_cmd(HCI_LINK_POLICY_OGF, HCI_SNIFF_MODE_OCF, sizeof(sniff_cmd), &sniff_cmd);
}

typedef struct {
	uint8_t type;
	uint16_t handle;
	uint16_t total_length;
	uint16_t data_length;
	uint16_t channel;
} __attribute__((packed)) bt_hci_acl_t;

void bt_acl_send(const uint16_t handle, const uint8_t PB, const uint8_t BC, const uint16_t channel, const uint16_t dlen, const void *dat)
{
	bt_hci_acl_t packet;

	// refuse any HCI if interface is not enabled
	if (mw_bt_is_enabled() == 0)
		return;

	packet.type = HCI_ACL_DATA_PACKET;
	packet.handle = handle | ((PB & 0x03) << 12) | ((BC & 0x03) << 14);
	packet.total_length = dlen + 4;
	packet.data_length = dlen;
	packet.channel = channel;
	mw_bt_uart_tx(&packet, sizeof(bt_hci_acl_t));
	mw_bt_uart_tx(dat, dlen);
}

void bt_hci_init(void)
{
	state = HCI_PACKET_START;
}

void bt_hci_ehcill_wake(void)
{
	const uint8_t ehcill_p = EHCILL_WAKE_UP_ACK;

	debug_uart_tx("HCILL wakeup\n");

	P1IE &= ~BT_IO_CTS;
	P1IFG &= ~BT_IO_CTS;
	state = HCI_PACKET_START;

	BT_IO_POUT &= ~BT_IO_RTS; // drop RTS -> start data

	mw_bt_uart_tx(&ehcill_p, 1);
	//__delay_cycles(160000);
}