#include "Bluetooth.h"
#include <Arduino.h>
#include <basic.h>
#include <MicroBitUARTService.h>

MicroBitUARTService *uart = NULL;

eventMBle connected = NULL;
eventMBle disConnected = NULL;
eventMBle uartDataReceived = NULL;

String delimiter = "^";
String terminator = "#";

bool EVENT_Frist =  true;
class LinkedKeyHandlerList {
  public:
    String key;
    ValueTypeIndicator type;
    eventUartStringMBle callback_s = NULL;
    eventUartNumberMBle callback_n = NULL;
    LinkedKeyHandlerList *next = NULL;
}; LinkedKeyHandlerList *handlers = NULL;


class TypeContainer {
  public:
    String stringValue;
    int numberValue;
}; 
TypeContainer *messageContainer = new TypeContainer();

DFMicrobitBle *_private_ble = NULL;



DFMicrobitBle::DFMicrobitBle()
{
  _private_ble = this;
}


// Microbit Bluetooth Button Service Program
void DFMicrobitBle::startButtonService()
{
  new MicroBitButtonService(*uBit.ble);
}
// Microbit bluetooth gesture service
void DFMicrobitBle::startAccelerometerService()
{
  new MicroBitAccelerometerService(*uBit.ble, uBit.accelerometer);
}
// Microbit bluetooth led service program
void DFMicrobitBle::startLEDService()
{
  new MicroBitLEDService(*uBit.ble, uBit.display);
}
// Microbit bluetooth temperature service
void DFMicrobitBle::startTemperatureService()
{
  new MicroBitTemperatureService(*uBit.ble, uBit.thermometer);
}
// Microbit bluetooth io service
void DFMicrobitBle::startIOPinService()
{
  new MicroBitIOPinService(*uBit.ble, uBit.io);
}
// Microbit bluetooth magnetometer service
void DFMicrobitBle::startMagnetometerService()
{
  new MicroBitMagnetometerService(*uBit.ble, uBit.compass);
}
//Open all bluetooth services
void DFMicrobitBle::openBluetoothService()
{
  this->startButtonService();
  this->startAccelerometerService();
  this->startLEDService();
  this->startTemperatureService();
  this->startIOPinService();
  this->startMagnetometerService();
}


// Interrupt event processing
void eventConnected(MicroBitEvent e)
{
  if(connected) {
    connected();
  }
}
void eventDisConnected(MicroBitEvent e)
{
  if(disConnected) {
    disConnected();
  }
}
void evenOnUartDataReceived(MicroBitEvent e)
{
  if(uartDataReceived) {
    uartDataReceived();
  }
}

// When the Bluetooth connection is successful, register a callback code to run
void DFMicrobitBle::onBluetoothConnected(eventMBle event)
{
  connected = event;
  uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_CONNECTED, eventConnected);
}


void DFMicrobitBle::onBluetoothDisconnected(eventMBle event)
{
  disConnected = event;
  uBit.messageBus.listen(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED, eventDisConnected);
}


void DFMicrobitBle::onUartDataReceived(String delimiters, eventMBle event) {
  uartDataReceived = event;
  this->startUartService();
  uart->eventOn(ManagedString(delimiters.c_str()));
  uBit.messageBus.listen(MICROBIT_ID_BLE_UART, MICROBIT_UART_S_EVT_DELIM_MATCH, evenOnUartDataReceived);
}

void DFMicrobitBle::sendMessageWithStringValue(String key, String value)
{
  this->sendRawMessage(key, type_String, value);
}

void DFMicrobitBle::sendMessageWithNumberValue(String key, int value)
{
  char string[32];
  itoa(value, string, 10);
  this->sendRawMessage(key, type_Number, (String)string);
}

void DFMicrobitBle::sendMessageWithNumberValue(String key, double value)
{
  char string[32];
  itoa(value, string, 10);
  this->sendRawMessage(key, type_Number, (String)string);
}



ValueTypeIndicator getValueTypeIndicatorForString(String typeString) {
  if(strcmp(typeString.c_str(), "S") == 0)
  {
    return type_String;
  }else if(strcmp(typeString.c_str(), "N") == 0)
  {
    return type_Number;
  }else{
    return (ValueTypeIndicator)NULL;
  }
}

void handleIncomingUARTData()
{
  //uBit.serial.printf("receive:\r\n");
  if(uart == NULL) uart = new MicroBitUARTService(*uBit.ble, 61, 60);
  String latestMessage = uart->readUntil(ManagedString("#")).toCharArray();
  //uBit.serial.printf("%s\r\n",latestMessage.c_str());

  String result[3];
  int count = 0;
  int startIndex = 0;
  int len = latestMessage.length();
  
  for (int index = 0; index < len; index++) {
    if (latestMessage.charAt(index) == '^') {
      /*char str[index - startIndex + 1];
      memcpy(str, input.c_str()+startIndex, index - startIndex);
      str[index - startIndex] = '\0';
      result[count] = (String)str;*/
      result[count] = latestMessage.substring(startIndex, startIndex + index - startIndex);
      startIndex = index + 1;
      count = count + 1;
    }
  }
  result[count] = latestMessage.substring(startIndex, startIndex + len - startIndex);

  ValueTypeIndicator type = getValueTypeIndicatorForString(result[0]);
  String key = result[1];
  String val = result[2];
  //uBit.serial.printf("%s\r\n",val.c_str());

  if (type == type_Number) {
    messageContainer->numberValue = val.c_str()[0] - 40;//parseInt(val);
  } else if (type == type_String) {
    messageContainer->stringValue = val;
  } else {
    messageContainer->stringValue = val;
  }

  LinkedKeyHandlerList *handlerToExamine = handlers;
  
  if (handlerToExamine == NULL) { //empty handler list
    //basic.showString("nohandler")
  }
  
  while (handlerToExamine != NULL) {
    if (handlerToExamine->key == key && handlerToExamine->type == type) {
      //handlerToExamine->callback(messageContainer)
      if(handlerToExamine->type == type_String){
        handlerToExamine->callback_s(messageContainer->stringValue);
      }else if(handlerToExamine->type == type_Number){
        handlerToExamine->callback_n(messageContainer->numberValue);
      }else{
        //do nothing;
      }
    }
    handlerToExamine = handlerToExamine->next;
  }
}

void bleHandle()
{
  while(true){ 
    handleIncomingUARTData();
    fiber_sleep(20);
  }
}

void DFMicrobitBle::onStringReceived(String key, eventUartStringMBle event)
{
  if(EVENT_Frist){
    create_fiber(bleHandle);
    EVENT_Frist = false;
  }

  LinkedKeyHandlerList *newHandler = new LinkedKeyHandlerList();

  newHandler->callback_s = event;
  newHandler->type = type_String;
  newHandler->key = key;
  newHandler->next = handlers;
  handlers = newHandler;
}

void DFMicrobitBle::onNumberReceived(String key, eventUartNumberMBle event)
{
  if(EVENT_Frist){
    create_fiber(bleHandle);
    EVENT_Frist = false;
  }

  LinkedKeyHandlerList *newHandler = new LinkedKeyHandlerList();

  newHandler->callback_n = event;
  newHandler->type = type_Number;
  newHandler->key = key;
  newHandler->next = handlers;
  handlers = newHandler;
}


void DFMicrobitBle::sendRawMessage(String key, ValueTypeIndicator type, String value)
{
  String indicatorAsString = this->getStringForValueTypeIndicator(type);
  this->uartWriteString(indicatorAsString + delimiter + key + delimiter + value + terminator);
}



void DFMicrobitBle::startUartService() {
  if (uart) return;
  // 61 octet buffer size is 3 x (MTU - 3) + 1
  // MTU on nRF51822 is 23 octets. 3 are used by Attribute Protocol header data leaving 20 octets for payload
  // So we allow a RX buffer that can contain 3 x max length messages plus one octet for a terminator character
  uart = new MicroBitUARTService(*uBit.ble, 61, 60);
}

String DFMicrobitBle::getStringForValueTypeIndicator(ValueTypeIndicator vti)
{
  switch (vti) {
    case type_Number:
      return "N";
    case type_String:
      return "S";
    default:
      return "!";
  }
}

void DFMicrobitBle::uartWriteString(String data) {
  this->startUartService();
  uart->send(ManagedString(data.c_str()));
}


DFMicrobitBle MicrobitBle;


