/*
  This file is part of the ArduinoBLE library.
  Copyright (c) 2018 Arduino SA. All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#if defined(ESP32)

#include "HCIVirtualTransport.h"

StreamBufferHandle_t rec_buffer;
StreamBufferHandle_t send_buffer;
TaskHandle_t bleHandle;


static void notify_host_send_available(void)
{
}

static int notify_host_recv(uint8_t *data, uint16_t length)
{
  xStreamBufferSend(rec_buffer,data,length,portMAX_DELAY);  // !!!potentially waiting forever
  return 0;
}

static esp_vhci_host_callback_t vhci_host_cb = {
  notify_host_send_available,
  notify_host_recv
};

void bleTask(void *pvParameters)
{
  esp_vhci_host_register_callback(&vhci_host_cb);
  size_t length;
  uint8_t mybuf[258];

  while(true){
    length = xStreamBufferReceive(send_buffer,mybuf,258,portMAX_DELAY);
    while (!esp_vhci_host_check_send_available()) {}
    esp_vhci_host_send_packet(mybuf, length);
  }
}


HCIVirtualTransportClass::HCIVirtualTransportClass()
{
}

HCIVirtualTransportClass::~HCIVirtualTransportClass()
{
}

int HCIVirtualTransportClass::begin()
{
  btStarted(); // this somehow stops the arduino ide from initializing bluedroid

  rec_buffer = xStreamBufferCreate(258, 1);
  send_buffer = xStreamBufferCreate(258, 1);

  esp_err_t ret = nvs_flash_init();
  if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
  }
  esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
  
#if CONFIG_IDF_TARGET_ESP32
  bt_cfg.mode = ESP_BT_MODE_BLE; //original esp32 chip
#else
  bt_cfg.bluetooth_mode = ESP_BT_MODE_BLE; //different api for newer models
#endif

  esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
  esp_bt_controller_init(&bt_cfg);
  esp_bt_controller_enable(ESP_BT_MODE_BLE);
  xTaskCreatePinnedToCore(&bleTask, "bleTask", 2048, NULL, 5, &bleHandle, 0);
  return 1;
}

void HCIVirtualTransportClass::end()
{
  vStreamBufferDelete(rec_buffer);
  vStreamBufferDelete(send_buffer);
  esp_bt_controller_disable();
  esp_bt_controller_deinit();
  vTaskDelete(bleHandle);
}

void HCIVirtualTransportClass::wait(unsigned long timeout)
{
  for (unsigned long start = (esp_timer_get_time() / 1000ULL); ((esp_timer_get_time() / 1000ULL) - start) < timeout;) {
    if (available()) {
      break;
    }
  }
}

int HCIVirtualTransportClass::available()
{
  size_t bytes	= xStreamBufferBytesAvailable(rec_buffer);
  return bytes;
}

// never called
int HCIVirtualTransportClass::peek()
{
  return -1;
}

int HCIVirtualTransportClass::read()
{
  uint8_t c;
  if(xStreamBufferReceive(rec_buffer, &c, 1, portMAX_DELAY)) {
    return c;
  }
  return -1;
}

size_t HCIVirtualTransportClass::write(const uint8_t* data, size_t length)
{
  size_t result = xStreamBufferSend(send_buffer,data,length,portMAX_DELAY);
  return result;
}

HCIVirtualTransportClass HCIVirtualTransport;

HCITransportInterface& HCITransport = HCIVirtualTransport;

#endif
