#include "../configuration.h"

#define MY_DEBUG
#define SCAN_COMPLETED_BIT		BIT0


#define MAX_DISCOVERED_DEVICES 	10
#define SCAN_LEN_SEC			5	//[sec]
#define PERC_SCAN_LEN			20.0f //[%] range: 0 - 100
/******************************************************************************
 * Private Types Definition
 ******************************************************************************/
typedef struct
{
	esp_bd_addr_t addr;
	int rssi;
}Info_devices_t;
/******************************************************************************
 * Private Variables Definition
 ******************************************************************************/
static esp_ble_scan_params_t ble_scan_params =
{
	.scan_type              = BLE_SCAN_TYPE_ACTIVE,
	.own_addr_type          = BLE_ADDR_TYPE_PUBLIC,
	.scan_filter_policy     = BLE_SCAN_FILTER_ALLOW_ALL,
	.scan_interval          = 0x50,
	.scan_window            = 0x30
};


static uint8_t discovered_devices_num = 0;

static SemaphoreHandle_t yMutex_scan = NULL;
static EventGroupHandle_t yScan_event_group;
static Info_devices_t discovered_devices[MAX_DISCOVERED_DEVICES];
/*****************************************************************************
 * Public Variables Definition
 ******************************************************************************/

/******************************************************************************
 * Functions prototypes
 ******************************************************************************/

static void addDevice(esp_bd_addr_t address, int rssi);

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);

/******************************************************************************
 * Private Functions Definition
 ******************************************************************************/

// add a new device to the list
static void addDevice(esp_bd_addr_t address, int rssi)
{
	discovered_devices_num++;
	if(discovered_devices_num < MAX_DISCOVERED_DEVICES)
	{
		for(int i = 0; i < ESP_BD_ADDR_LEN; i++)
				discovered_devices[discovered_devices_num - 1].addr[i] = address[i];

		discovered_devices[discovered_devices_num - 1].rssi = rssi;
	}
}

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    if(event == ESP_GAP_BLE_SCAN_RESULT_EVT)
    {
    	switch(param->scan_rst.search_evt)
    	{
    		case ESP_GAP_SEARCH_INQ_RES_EVT:
					printf("ESP_GAP_BLE_SCAN_RESULT_EVT\n");
					printf("Device found: ADDR=");
					for(int i = 0; i < ESP_BD_ADDR_LEN; i++)
					{
						printf("%02X", param->scan_rst.bda[i]);
						if(i != ESP_BD_ADDR_LEN -1)
							printf(":");
					}
					printf(" RSSI=%d", param->scan_rst.rssi);
					printf("\n\n");

					addDevice(param->scan_rst.bda, param->scan_rst.rssi);
    		break;

    		case ESP_GAP_SEARCH_INQ_CMPL_EVT:
    			printf("Scan complete\n\n");
    			xEventGroupSetBits(yScan_event_group, SCAN_COMPLETED_BIT);
    		break;

    		default:
    		break;
    	}
    }
}

void init_ble_scan (void)
{
	// register GAP callback function
	ESP_ERROR_CHECK(esp_ble_gap_register_callback(esp_gap_cb));

	// configure scan parameters
	esp_ble_gap_set_scan_params(&ble_scan_params);

	yMutex_scan = xSemaphoreCreateMutex();
	yScan_event_group = xEventGroupCreate();
}

void init_ble (void)
{
	// release memory reserved for classic BT (not used)
	ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

	// initialize the BT controller with the default config
	esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
	esp_bt_controller_init(&bt_cfg);

	// enable the BT controller in BLE mode
	esp_bt_controller_enable(ESP_BT_MODE_BLE);

	// initialize Bluedroid library
	esp_bluedroid_init();
	esp_bluedroid_enable();
}

void init_ble_collaudo (void)
{
	init_ble();

	init_ble_scan();
}

bool ble_scan_collaudo(uint16_t *pzRetVal, uint16_t zPar)
{
	(void) zPar;
	bool zRetVal = false;

	xSemaphoreTake( yMutex_scan, portMAX_DELAY );

	esp_ble_gap_start_scanning(SCAN_LEN_SEC);
	EventBits_t bits = xEventGroupWaitBits(yScan_event_group,
				SCAN_COMPLETED_BIT,
				pdTRUE,
	            pdFALSE,
	            pdMS_TO_TICKS(SCAN_LEN_SEC * 1000 * (1 + PERC_SCAN_LEN/100)));

	if(bits & SCAN_COMPLETED_BIT)
	{
		zRetVal = true;
		if(discovered_devices_num == 0)
		{
			*pzRetVal = 0;
		}
		else
		{
			*pzRetVal = discovered_devices[0].rssi * (-1);
		}
	}

	discovered_devices_num = 0;
	esp_ble_gap_stop_scanning();
	xSemaphoreGive( yMutex_scan );

	return zRetVal;
}

void init_wifi_collaudo(void)
{
	/**
	 * 1. Wi-Fi/LwIP init phase
	 */
	esp_netif_init(); // 1.1 create LwIP core task and initialize LwIP related work

	esp_event_loop_create_default(); // 1.2 create system event task and initialize an application event's callbask function

	esp_netif_create_default_wifi_sta(); // 1.3 create default network interface instance binding station withc TCP/IP stack

	wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); // 1.4 create wifi driver task and initialize wifi driver
	esp_wifi_init(&cfg);

	esp_wifi_set_mode(WIFI_MODE_STA); // 1.5 configure wifi mode as station

	/**
	 * 3. Wi-Fi start phase
	 */
	esp_wifi_start(); // start wifi driver

}

bool scan_wifi_collaudo(uint16_t *pzRetVal, uint16_t zPar)
{
	(void) zPar;

	bool zRetVal = false;
	esp_err_t zScanRetVal = ESP_OK;
	uint16_t number = 5;
	wifi_ap_record_t ap_info[5];
	uint16_t ap_count = 0;

#ifdef MY_DEBUG
	printf("Scan wifi\n");
#endif

	zScanRetVal = esp_wifi_scan_start(NULL, true);

	if(zScanRetVal == ESP_OK)
	{
		zScanRetVal = esp_wifi_scan_get_ap_records(&number, ap_info);
	}

	if(zScanRetVal == ESP_OK)
	{
		zScanRetVal = esp_wifi_scan_get_ap_num(&ap_count);
	}

	if(zScanRetVal == ESP_OK)
	{
#ifdef MY_DEBUG
		for(uint8_t i = 0; i < ap_count && i < 5; i++)
		{
			printf("SSID: %s RSSI: %d\n", ap_info[i].ssid, ap_info[i].rssi);
		}
#endif
		if(ap_count != 0)
		{
			*pzRetVal = ap_info[0].rssi * (-1);
		}
		else
		{
			*pzRetVal = 0;
		}

		zRetVal = true;
	}

	return zRetVal;
}

void app_main(void)
{
	ESP_ERROR_CHECK(nvs_flash_init());

	init_ble_collaudo();

	init_wifi_collaudo();

	while(1)
	{
		uint16_t i = 0;
		ble_scan_collaudo(&i, 0);
		printf("rssi ble: %d\n", i);
		vTaskDelay(pdMS_TO_TICKS(5000));
		esp_bt_controller_disable();
		scan_wifi_collaudo(&i, 0);
		printf("rssi wifi: %d\n", i);
		esp_bt_controller_enable(ESP_BT_MODE_BLE);
		vTaskDelay(pdMS_TO_TICKS(5000));
	}
}
