/*
 * Copyright 2015, Broadcom Corporation
 * All Rights Reserved.
 *
 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
 * the contents of this file may not be disclosed to third parties, copied
 * or duplicated in any form, in whole or in part, without the prior
 * written permission of Broadcom Corporation.
 */

/** @file
*
* uart demo for the anaren a20737-MSDB1 board.
*/

#define CONFIG_IN_NVRAM 1
#include "bleprofile.h"
#include "bleapp.h"
#include "gpiodriver.h"
#include "stdio.h"
#include "platform.h"
#include "bleappconfig.h"
#include "cfa.h"
#include "spar_utils.h"
#include "puart.h"
#include "devicelpm.h"
// To use PWM, we will need the auxiliary clock and the PWM drier.
#include "aclk.h"
#include "pwm.h"
#include "i2cm.h"
#include <inttypes.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "utils.h"

/******************************************************
 *                      Constants
 ******************************************************/
// The GPIO to which an LED is assumed to be connected. Note that
// the tag board does not have an LED connected to P26 by default.
const BYTE RED_LED = GPIO_PIN_P26;
const BYTE GREEN_LED = GPIO_PIN_P27;
const BYTE BLUE_LED = GPIO_PIN_P28;
const BYTE BUZZER = GPIO_PIN_P14;
const BYTE IOEXPAND_INT = GPIO_PIN_2;
BLE_PROFILE_CFG puart_control_cfg = {0};

// Please note that all UUIDs need to be reversed when publishing in the database
#define SERVICE_1_UUID                  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define SERVICE_1_CHARACTERISTIC_1_UUID 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define SERVICE_1_CHARACTERISTIC_2_UUID 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define SERVICE_1_CHARACTERISTIC_3_UUID 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

#define SERVICE_2_UUID                  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define SERVICE_2_CHARACTERISTIC_1_UUID 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

#define SERVICE_3_UUID                  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
#define SERVICE_4_UUID                  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00







/******************************************************
 *               Types
 ******************************************************/

/******************************************************
 *               Function Prototypes
 ******************************************************/
static void app(void);
static void timer_cb(UINT32 value);
static void fine_timer_cb(UINT32 value);
static UINT8* showActivity(UINT8 *buffer);
static void ioexpand_int_handler(void *data, UINT8 pin);

/******************************************************
 *               Variables Definitions
 ******************************************************/
const char BLE_NAME[] = "BCM20737";
const char DEV_VERSION[] = "0.01";
const UINT8 gatt_database[] =
{
		//This service contains the device name
		//A read/write characteristic
		//The raw temp with ntf available
		PRIMARY_SERVICE_UUID128(0x0010,SERVICE_1_UUID),
		CHARACTERISTIC_UUID128(0x0011, 0x0012, SERVICE_1_CHARACTERISTIC_1_UUID,
		LEGATTDB_CHAR_PROP_READ,
		LEGATTDB_PERM_READABLE, 8),
		'B','C','M','2','0','7','3','7',

		CHARACTERISTIC_UUID128_WRITABLE(0x0013, 0x0014, SERVICE_1_CHARACTERISTIC_2_UUID,
		LEGATTDB_CHAR_PROP_READ|LEGATTDB_CHAR_PROP_WRITE,
		LEGATTDB_PERM_READABLE|LEGATTDB_PERM_WRITABLE|LEGATTDB_PERM_WRITE_REQ, 16),
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

		CHARACTERISTIC_UUID128_WRITABLE(0x0015, 0x0016, SERVICE_1_CHARACTERISTIC_3_UUID,
		LEGATTDB_CHAR_PROP_READ|LEGATTDB_CHAR_PROP_WRITE|LEGATTDB_CHAR_PROP_NOTIFY,
		LEGATTDB_PERM_READABLE|LEGATTDB_PERM_WRITABLE|LEGATTDB_PERM_WRITE_REQ, 18),
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

		CHAR_DESCRIPTOR_UUID16_WRITABLE(0x17,
		UUID_DESCRIPTOR_CLIENT_CHARACTERISTIC_CONFIGURATION,
		LEGATTDB_PERM_READABLE | LEGATTDB_PERM_WRITE_CMD | LEGATTDB_PERM_WRITE_REQ,
		2),
		0x00,0x00,

		CHAR_DESCRIPTOR_UUID16(0x18,
		UUID_DESCRIPTOR_CHARACTERISTIC_USER_DESCRIPTION,
		LEGATTDB_PERM_READABLE,
		11),
		'T','e','m','p','e','r','a','t','u','r','e',

		//This service contains a characteristic to enable the buzzer
		PRIMARY_SERVICE_UUID128(0x0020,SERVICE_2_UUID),
		CHARACTERISTIC_UUID128_WRITABLE(0x0021, 0x0022, SERVICE_2_CHARACTERISTIC_1_UUID,
		LEGATTDB_CHAR_PROP_READ | LEGATTDB_CHAR_PROP_WRITE,
		LEGATTDB_PERM_READABLE|LEGATTDB_PERM_WRITABLE|LEGATTDB_PERM_WRITE_REQ, 1),
		0x00,

		CHAR_DESCRIPTOR_UUID16(0x23,
		UUID_DESCRIPTOR_CHARACTERISTIC_USER_DESCRIPTION,
		LEGATTDB_PERM_READABLE,
		6),
		'B','u','z','z','e','r',

		PRIMARY_SERVICE_UUID128(0x0030,SERVICE_3_UUID),
		PRIMARY_SERVICE_UUID128(0x0040,SERVICE_4_UUID),

};

BD_ADDR ble_device_addr = {0};
uint8_t run = 1;
uint8_t temp = 0.0;
UINT8 activityBuffer[4] = {'.',' ',' ',0};
char recvBuffer[20];
BYTE numConnectedClients = 0;

/******************************************************
 *               Function Definitions
 ******************************************************/
static void serial_rxCb(void *unused)
{
	UINT8 character = 0;
	uint8_t i = 0;

	while (puart_rxFifoNotEmpty() && puart_read(&character))
	{
		puart_write(character);
		recvBuffer[i++ % 20] = character;
		bleapputils_delayUs(500); //Needed to let some time to the uart to receive the data
	}
	recvBuffer[i] = '\0';

	// clear the interrupt
	P_UART_INT_CLEAR(P_UART_ISR_RX_AFF_MASK);
	// enable UART interrupt in the Main Interrupt Controller and RX Almost Full in the UART Interrupt Controller
	P_UART_INT_ENABLE |= P_UART_ISR_RX_AFF_MASK;

	if(memcmp(recvBuffer,"stop", 4) == 0)
		run = 0;
	else if(memcmp(recvBuffer,"start", 4) == 0)
		run = 1;
}

static void ioexpand_int_handler(void *data, UINT8 pin)
{
	puart_print("IO int\n");
}

static void ble_link_up(void)
{
	puart_print("BLE Link up\n");
	numConnectedClients++;
}

static void ble_link_down(void)
{
	puart_print("BLE Link down\n");

	//Mandatory to be able to reconnect once disconnected
	bleprofile_Discoverable(HIGH_UNDIRECTED_DISCOVERABLE, ble_device_addr);

	numConnectedClients--;
	if(!numConnectedClients)gpio_setPinOutput(PIN2PORT(RED_LED), PIN2PIN(RED_LED), 0);
}

static INT32 ble_write_handler(LEGATTDB_ENTRY_HDR *p)
{
	UINT16 handle = legattdb_getHandle(p);
	int len = legattdb_getAttrValueLen(p), i = 0;
	UINT8 *buffer = legattdb_getAttrValue(p);

	puart_printf("Handle : 0x%02X\n", handle);
	puart_print("Client wrote (HEX) : #");
	for(i = 0; i < len; i++)
	{
		puart_printf("0x%02X ",buffer[i]);
	}
	puart_print("#\n");

	puart_print("Client wrote (ASCII) : #");
	for(i = 0; i < len; i++)
	{
		puart_write(buffer[i]);
	}
	puart_print("#\n");

	switch(handle)
	{
	case 0x17:
		if(len == 2)
		{
			if(buffer[0] & CCC_NOTIFICATION)
				puart_print("NTF enabled\n");
			else
				puart_print("NTF disabled\n");
		}
		break;
	case 0x22:
		if(buffer[0])
		{
			puart_print("Buzzer on\n");
			gpio_configurePin(PIN2PORT(BUZZER), PIN2PIN(BUZZER), PWM2_OUTPUT_ENABLE_P14 , 0);
			pwm_start(PWM2, PMU_CLK, 0x2FF, 0x200);
		}
		else
		{
			puart_print("Buzzer off\n");
			gpio_configurePin(PIN2PORT(BUZZER), PIN2PIN(BUZZER), GPIO_OUTPUT_DISABLE | GPIO_PULL_DOWN, 0);
			pwm_setReset(PWM2, 1);
		}
		break;
	default:
		break;
	}

	return 0;
}

// Application initialization
APPLICATION_INIT()
{
	puart_control_cfg.fine_timer_interval = 1000;
	puart_control_cfg.default_adv = HIGH_UNDIRECTED_DISCOVERABLE;
	puart_control_cfg.button_adv_toggle = 0;
	puart_control_cfg.high_undirect_adv_interval = 32;
	puart_control_cfg.low_undirect_adv_interval = 1024;
    puart_control_cfg.high_undirect_adv_duration = 30;   // seconds
    puart_control_cfg.low_undirect_adv_duration = 300;  // seconds
    puart_control_cfg.high_direct_adv_interval = 0;    // seconds
    puart_control_cfg.low_direct_adv_interval = 0;    // seconds
    puart_control_cfg.high_direct_adv_duration = 0;    // seconds
    puart_control_cfg.low_direct_adv_duration = 0;    // seconds
	memcpy(puart_control_cfg.local_name, BLE_NAME, sizeof(BLE_NAME));
	puart_control_cfg.cod[0] = (UINT8) APPEARANCE_GENERIC_TAG ;
	puart_control_cfg.cod[1] = APPEARANCE_GENERIC_TAG >> 8;
	memcpy(puart_control_cfg.ver, DEV_VERSION, sizeof(DEV_VERSION));
	memset(puart_control_cfg.hdl, 0, HANDLE_NUM_MAX);
	memset(puart_control_cfg.serv, 0, HANDLE_NUM_MAX);
	memset(puart_control_cfg.cha, 0, HANDLE_NUM_MAX);

	puart_control_cfg.tx_power_level = 4; // max is +4 dBm...
	memset(recvBuffer, 0, sizeof(recvBuffer));


    bleapp_set_cfg((UINT8 *)gatt_database,
                       sizeof(gatt_database),
                       (void *)&puart_control_cfg,
                       (void *)NULL,
                       (void *)NULL,
                       app);
}

UINT32 uart_device_lpm_queriable(LowPowerModePollType type, UINT32 context)
{
    // Disable sleep.
    return 0;
}

// Makes the litle dot move at the end of the temperature string.
UINT8* showActivity(UINT8 *buffer)
{
	if(buffer[0] == '.')
	{
		buffer[0] = ' ';
		buffer[1] = '.';
		buffer[2] = ' ';
	}
	else if(buffer[1] == '.')
	{
		buffer[0] = ' ';
		buffer[1] = ' ';
		buffer[2] = '.';
	}
	else
	{
		buffer[0] = '.';
		buffer[1] = ' ';
		buffer[2] = ' ';
	}
	return buffer;
}

void app(void)
{
	devlpm_init();

	//UART INIT SECTION
	puart_setBaudrate(0,0,115200);
	puart_selectUartPads(((GPIO_PIN_UART_RX / 16) << 5) | (GPIO_PIN_UART_RX % 16), ((GPIO_PIN_UART_TX / 16) << 5) | (GPIO_PIN_UART_TX % 16), 0 ,0);
	puart_init();
	puart_flowOff();
	devlpm_registerForLowPowerQueries(uart_device_lpm_queriable, 0);
	P_UART_INT_CLEAR(P_UART_ISR_RX_AFF_MASK);
	P_UART_WATER_MARK_RX_LEVEL(1);
	P_UART_INT_ENABLE |= P_UART_ISR_RX_AFF_MASK;
	puart_rxCb = serial_rxCb;
	puart_enableInterrupt();

	//BLE INIT SECTION
	bleprofile_Init(bleprofile_p_cfg);
	bleprofile_regTimerCb(fine_timer_cb, timer_cb);
	bleprofile_StartTimer();

	bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_UP, ble_link_up);
	bleprofile_regAppEvtHandler(BLECM_APP_EVT_LINK_DOWN, ble_link_down);
	legattdb_regWriteHandleCb((LEGATTDB_WRITE_CB)ble_write_handler);

	bleprofile_Discoverable(HIGH_UNDIRECTED_DISCOVERABLE, ble_device_addr);

	gpio_configurePin(PIN2PORT(RED_LED), PIN2PIN(RED_LED), GPIO_OUTPUT_ENABLE, GPIO_PIN_OUTPUT_LOW);
	gpio_configurePin(PIN2PORT(GREEN_LED), PIN2PIN(GREEN_LED), GPIO_OUTPUT_ENABLE, GPIO_PIN_OUTPUT_LOW);
	gpio_configurePin(PIN2PORT(BLUE_LED), PIN2PIN(BLUE_LED), GPIO_OUTPUT_ENABLE, GPIO_PIN_OUTPUT_LOW);
	gpio_configurePin(PIN2PORT(IOEXPAND_INT), PIN2PIN(IOEXPAND_INT), GPIO_INPUT_ENABLE | GPIO_PULL_UP | GPIO_EN_INT_FALLING_EDGE, GPIO_PIN_INPUT_HIGH);
	//gpio_registerForInterrupt(NULL, ioexpand_int_handler, NULL);
	//Buzzer config
	aclk_configure(512000, ACLK1, ACLK_FREQ_24_MHZ);
	//I2C Bus init
	i2cm_init();
	i2cm_setSpeed(I2CM_SPEED_400KHZ);

	puart_print("App init done\r\n");
}

void timer_cb(UINT32 value)
{
	static uint8_t state = 0;

	if(run)
		state++;

	switch(state%2)
	{
	case 0:
		gpio_setPinOutput(PIN2PORT(GREEN_LED), PIN2PIN(GREEN_LED), 1);
		gpio_setPinOutput(PIN2PORT(BLUE_LED), PIN2PIN(BLUE_LED), 0);
		break;
	default:
		gpio_setPinOutput(PIN2PORT(GREEN_LED), PIN2PIN(GREEN_LED), 0);
		gpio_setPinOutput(PIN2PORT(BLUE_LED), PIN2PIN(BLUE_LED), 1);

		//I2C test
		uint8_t reg = 0xFE;
		uint8_t ManuID[2];
		uint16_t raw_temp = 0;

		if(i2cm_comboRead(ManuID, 2, &reg, 1, 0x40 << 1) == I2CM_SUCCESS)
		{
			puart_print("I2C comboRead success\n");
			puart_printf("Manu ID : 0x%02X%02X\n", ManuID[0], ManuID[1]);

			reg = 0x01;
			i2cm_comboRead(ManuID, 2, &reg, 1, 0x40 << 1);
			raw_temp = (ManuID[0] << 6) + (ManuID[1] >> 2);
			temp = raw_temp / 32;
			puart_printf("Raw temp : 0x%02X%02X -> %u, %u\n", ManuID[0], ManuID[1], raw_temp, temp);
		}
		else
			puart_print("I2C comboRead failure\n");

	}

	if(numConnectedClients)
	{
		gpio_togglePin(PIN2PORT(RED_LED), PIN2PIN(RED_LED));
	}
}

void fine_timer_cb(UINT32 value)
{
	BLEPROFILE_DB_PDU db_pdu;

	UINT8 buffer[18];
	sprintf((char*)buffer,"temp : %u C%s", temp, showActivity(activityBuffer));

	puart_printf("%s\nrecvBuffer : #%s#\n",buffer, recvBuffer);

	bleprofile_ReadHandle(0x0016, &db_pdu);
	memcpy(db_pdu.pdu, buffer, sizeof(buffer));
	db_pdu.len = strlen(buffer);

	bleprofile_WriteHandle(0x0016, &db_pdu);
	bleprofile_sendNotification(0x0016, (UINT8 *)db_pdu.pdu, db_pdu.len);
}
