/**
  ******************************************************************************
  * @file    sample_service.c
  * @author  MCD Application Team
  * @version V1.0.0
  * @date    04-July-2014
  * @brief   Add a sample service using a vendor specific profile.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2014 STMicroelectronics</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include <automation.h>
#include "sample_service.h"
#include "bluenrg_gap_aci.h"
#include "bluenrg_gatt_aci.h"
#include "bluenrg_hal_aci.h"

#include "main.h"
#include "rtc_if.h"
#include <string.h>
#include <stdlib.h>

#include "signals.h"
#include "parcelize.h"
#include "crc32.h"


#define CHUNK_SIZE 17

/** @addtogroup Applications
 *  @{
 */

/** @addtogroup SampleApp
 *  @{
 */

/** @defgroup SAMPLE_SERVICE
 * @{
 */

/** @defgroup SAMPLE_SERVICE_Private_Variables
 * @{
 */
/* Private variables ---------------------------------------------------------*/
volatile int connected = FALSE;
volatile uint8_t set_connectable = 1;
volatile uint16_t connection_handle = 0;
volatile uint8_t notification_tx_enabled = FALSE, notification_fsm_enabled = FALSE,
		notification_err_enabled = FALSE;

uint16_t tx_handle;
uint16_t rx_handle;
uint16_t date_handle;
uint16_t state_handle;
uint16_t err_handle;

uint16_t sampleServHandle, TXCharHandle, RXCharHandle, DateCharHandle, StateCharHandle, ErrCharHandle;

extern RTC_HandleTypeDef hrtc;
extern FiniteStateMachine finiteStateMachine;

bool uploadingInProgress = false;
int dataSize = 0;
int totalDataTransfer = 0;
long crcDataFromSender = 0;
char uploadBuffer[SETTINGS_BUFFER_SIZE];

uint16_t calib_buffer[6] = {0};

extern FLASH_STRUCT Flash;
extern uint16_t programDuration[];
extern WATERING_GENERAL default_settings;
extern volatile uint16_t outputs74HC955;

/**
 * @}
 */

/** @defgroup SAMPLE_SERVICE_Private_Macros
 * @{
 */
/* Private macros ------------------------------------------------------------*/
#define COPY_UUID_128(uuid_struct, uuid_15, uuid_14, uuid_13, uuid_12, uuid_11, uuid_10, uuid_9, uuid_8, uuid_7, uuid_6, uuid_5, uuid_4, uuid_3, uuid_2, uuid_1, uuid_0) \
  do {\
  	uuid_struct.uuid128[0] = uuid_0; uuid_struct.uuid128[1] = uuid_1; uuid_struct.uuid128[2] = uuid_2; uuid_struct.uuid128[3] = uuid_3; \
	uuid_struct.uuid128[4] = uuid_4; uuid_struct.uuid128[5] = uuid_5; uuid_struct.uuid128[6] = uuid_6; uuid_struct.uuid128[7] = uuid_7; \
	uuid_struct.uuid128[8] = uuid_8; uuid_struct.uuid128[9] = uuid_9; uuid_struct.uuid128[10] = uuid_10; uuid_struct.uuid128[11] = uuid_11; \
	uuid_struct.uuid128[12] = uuid_12; uuid_struct.uuid128[13] = uuid_13; uuid_struct.uuid128[14] = uuid_14; uuid_struct.uuid128[15] = uuid_15; \
	}while(0)
/**
 * @}
 */

/** @defgroup SAMPLE_SERVICE_Exported_Functions
 * @{
 */

static void sendCommandToOutputs( uint16_t id, ActuatorStates changeState, uint8_t pumpRate );
static void sendCommandToAutomation( uint16_t id, ActuatorStates changeState );
static void sendCommandToSystem( CommandOpcode opcode, uint16_t param[6] );
/**
 * @brief  Add a sample service using a vendor specific profile
 * @param  None
 * @retval Status
 */
tBleStatus Add_Sample_Service(void)
{
	  tBleStatus ret;

	  /*
	  UUIDs:
	  D973F2E0-B19E-11E2-9E96-0800200C9A66
	  D973F2E1-B19E-11E2-9E96-0800200C9A66
	  D973F2E2-B19E-11E2-9E96-0800200C9A66

	  D973F2E3-B19E-11E2-9E96-0800200C9A66
	  D973F2E4-B19E-11E2-9E96-0800200C9A66
	  D973F2E5-B19E-11E2-9E96-0800200C9A66

	  */

	  const uint8_t service_uuid[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe0,0xf2,0x73,0xd9};
	  const uint8_t charUuidTX[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe1,0xf2,0x73,0xd9};
	  const uint8_t charUuidRX[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe2,0xf2,0x73,0xd9};
	  const uint8_t charUuidDate[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe3,0xf2,0x73,0xd9};
	  const uint8_t charUuidState[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe4,0xf2,0x73,0xd9};
	  const uint8_t charUuidErr[16] = {0x66,0x9a,0x0c,0x20,0x00,0x08,0x96,0x9e,0xe2,0x11,0x9e,0xb1,0xe5,0xf2,0x73,0xd9};

	  ret = aci_gatt_add_serv(UUID_TYPE_128, service_uuid, PRIMARY_SERVICE, 16, &sampleServHandle); /* 1 for service + 3 for each characteristics */
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidTX, 20, CHAR_PROP_NOTIFY, ATTR_PERMISSION_NONE, 0,
	                           16, 1, &TXCharHandle);
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidRX, 20, CHAR_PROP_WRITE|CHAR_PROP_WRITE_WITHOUT_RESP, ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE,
	                           16, 1, &RXCharHandle);
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  //ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidTemp, 10, CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,
	  //                         16, 1, &TempCharHandle);

	  ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidDate, 13,  CHAR_PROP_READ, ATTR_PERMISSION_NONE, GATT_NOTIFY_READ_REQ_AND_WAIT_FOR_APPL_RESP,
	                           16, 1, &DateCharHandle);
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidState, 2, CHAR_PROP_NOTIFY | CHAR_PROP_WRITE_WITHOUT_RESP, ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE,
	                           16, 1, &StateCharHandle);
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  ret =  aci_gatt_add_char(sampleServHandle, UUID_TYPE_128, charUuidErr, 2, CHAR_PROP_NOTIFY, ATTR_PERMISSION_NONE, 0,
	                           16, 1, &ErrCharHandle);
	  if (ret != BLE_STATUS_SUCCESS) goto fail;

	  return BLE_STATUS_SUCCESS;

	fail:
	  APP_LOG(TS_OFF, VLEVEL_M, "Error while adding I-Spritz Service.\r\n");
	  return BLE_STATUS_ERROR ;
}

/**
 * @brief  Make the device connectable
 * @param  None
 * @retval None
 */
void Make_Connection(void)
{
  tBleStatus ret;

    const char local_name[] = {AD_TYPE_COMPLETE_LOCAL_NAME,'i','-','s','p','r','i','t','z'};

    /* disable scan response */
    hci_le_set_scan_resp_data(0,NULL);

    APP_LOG(TS_OFF, VLEVEL_M, "General Discoverable Mode ");
    /*
    Advertising_Event_Type, Adv_Interval_Min, Adv_Interval_Max, Address_Type, Adv_Filter_Policy,
    Local_Name_Length, Local_Name, Service_Uuid_Length, Service_Uuid_List, Slave_Conn_Interval_Min,
    Slave_Conn_Interval_Max
    */
    /*ret = aci_gap_set_discoverable(ADV_DATA_TYPE, ADV_INTERV_MIN, ADV_INTERV_MAX, PUBLIC_ADDR,
                                   NO_WHITE_LIST_USE, 9, local_name, 0, NULL, 0, 0);*/
    ret = aci_gap_set_discoverable(ADV_DATA_TYPE, 0, 0, PUBLIC_ADDR,
                                   NO_WHITE_LIST_USE, 9, local_name, 0, NULL, 0, 0);
    APP_LOG(TS_OFF, VLEVEL_M, "%d\r\n",ret);
}

/**
 * @brief  This function is used to receive data related to the sample service
 *         (received over the air from the remote board).
 * @param  data_buffer : pointer to store in received data
 * @param  Nb_bytes : number of bytes to be received
 * @retval None
 */
void receiveData(uint8_t* data_buffer, uint8_t Nb_bytes)
{
  char out[10];
  time_t lastCurrentDayCycle[4];

  if( uploadingInProgress )
  {
	  strncat(&uploadBuffer[totalDataTransfer],(char*)data_buffer,Nb_bytes);
	  totalDataTransfer += Nb_bytes;
	  if( totalDataTransfer == dataSize )
	  {
		  memcpy(lastCurrentDayCycle,Flash.paramSys.currentDayCycle,sizeof(Flash.paramSys.currentDayCycle));
		  uint32_t crc = calcCRC32stm(uploadBuffer, dataSize, 0x04C11DB7, 0xffffffff, 1, 1, 1);
		  uploadingInProgress = false;
		  bool transferOK = (crc == crcDataFromSender);
		  sprintf(out,"#U=%d", transferOK ? 1 : 0);
		  sendData( (uint8_t*)out, strlen(out) );
		  APP_LOG(TS_OFF, VLEVEL_M, "\r\n%s\r\n", uploadBuffer);
		  if( transferOK )
		  {
			  //WATERING_GENERAL new_settings;
			  GeneralUnparcelize( uploadBuffer, &Flash.settings );
			  initCurrentDayCycle(&lastCurrentDayCycle);
			  saveSettings( &Flash );
		  }
	  }
  }
  else
  {
	  for(int i = 0; i < Nb_bytes; i++) {
		APP_LOG(TS_OFF, VLEVEL_M, "%c", data_buffer[i]);
	  }
	  fflush(stdout);
  }
}

void setDefaultSettings()
{
	Flash.settings = default_settings;
	initCurrentDayCycle(NULL);
	saveSettings( &Flash );
}

void downloadingSettings()
{
	char out[50];

	char* se_buff = (char*)pvPortMalloc(SETTINGS_BUFFER_SIZE);
	if( !se_buff )
	{
		APP_LOG(TS_OFF, VLEVEL_M, "Not enough memory\r\n");
	}
	else
	{
		memset(se_buff,0,SETTINGS_BUFFER_SIZE);
		int size = GeneralParcelize(se_buff);
		uint32_t crc = calcCRC32stm(se_buff, size, 0x04C11DB7, 0xffffffff, 1, 1, 1);
		sprintf(out,"#D=%04d,%08lX",size,crc);
		sendData( (uint8_t*)out, strlen(out) );
		// Calculate chunk number
		uint16_t nChunk = size / CHUNK_SIZE;
		uint16_t remain = size % CHUNK_SIZE;
		if( remain > 0 )
			nChunk += 1;

		// Sending data
		for( int c=0; c < nChunk; c++ )
		{
			out[1] = c+1;
			if( c == nChunk - 1 )
			{
				out[0]=51;
				size = remain;
				if( !size )
					size = CHUNK_SIZE;
			}
			else
			{
				out[0]=50;
				size = CHUNK_SIZE;
			}
			strncpy(&out[2],&se_buff[c*CHUNK_SIZE],size);
			out[size+2] = '\0';
			//APP_LOG(TS_OFF, VLEVEL_M, out);
			sendData( (uint8_t*)out, strlen(out) );
			osDelay(50);
		}

		vPortFree(se_buff);
	}
}

void uploadingSettings( char* data_buffer )
{
	char out[10] = {0};

	dataSize = toInt( data_buffer, 0, 4 );
	substring(data_buffer,out,5,13);
	crcDataFromSender = strtoul(out,0,16);
	uploadingInProgress = true;
	totalDataTransfer = 0;
	memset(uploadBuffer,0,sizeof(uploadBuffer));
}


static void sendCommandToAutomation( uint16_t id, ActuatorStates changeState )
{
osMessageQId destQueue;
//BaseType_t xHigherPriorityTaskWoken;

	destQueue = (osMessageQId)getQueueId(AUTOMATION);
	//xHigherPriorityTaskWoken = pdFALSE;

	MSG_STRUCT *p_sendMsg = allocMemoryPoolMessage(); // receiver must free
	p_sendMsg->opcode = OP_MAIN;
	p_sendMsg->outputs = id;
	p_sendMsg->changeState = changeState;
    //xQueueSendFromISR(destQueue, &p_sendMsg, &xHigherPriorityTaskWoken);
	osMessagePut(destQueue, (uint32_t)p_sendMsg, osWaitForever);
}

void receiveDateTime(char* data_buffer)
{
struct tm* localTime;

	//time_t utc = (time_t)atol(data_buffer);
	time_t utc = (time_t)toLongLen( data_buffer, 10 );
	Flash.settings.timeZoneOffset = (int8_t)toIntLen( &data_buffer[12], 3);

	// utc += (Flash.settings.timeZoneOffset*3600);	// Don't work

	localTime = localtime(&utc);
	set_time( localTime->tm_mday, localTime->tm_mon+1, localTime->tm_year-100, localTime->tm_hour, localTime->tm_min, localTime->tm_sec);
	APP_LOG(TS_OFF, VLEVEL_M, "Setting date & time to %s\r\n",asctime(localTime));
	sendCommandToAutomation( 0b11111111111, Z_UPDATE );
}

static void sendCommandToOutputs( uint16_t id, ActuatorStates changeState, uint8_t pumpRate )
{
osMessageQId destQueue;

	destQueue = (osMessageQId)getQueueId(OUTPUTS);

	MSG_STRUCT *p_sendMsg = allocMemoryPoolMessage(); // receiver must free
	p_sendMsg->opcode = OP_OUTPUT_ONCHANGE;
	p_sendMsg->outputs = id;
	p_sendMsg->changeState = changeState;
	p_sendMsg->pumpRate = pumpRate;
	osMessagePut(destQueue, (uint32_t)p_sendMsg, osWaitForever);
}

void driveManyValves(char* data_buffer)
{
	uint8_t actuatorId = toIntLen( data_buffer, 3 );
	uint8_t avgPumpRate = toIntLen( &data_buffer[4], 3);
	//APP_LOG(TS_OFF, VLEVEL_M, "Bluetooth buffer: %s\r\n",data_buffer);
	sendCommandToOutputs( actuatorId, Z_UPDATE, avgPumpRate );
}

void emergencyStop()
{
	sendCommandToAutomation( ALL_OUTPUTS_OFF, E_STOP );
}

void safeStop()
{
	sendCommandToAutomation( ALL_OUTPUTS_OFF, Z_ALL_OFF );
}

void getWateringStats()
{
	uint8_t buff[1024] = {0}, out[30];
	struct tm current_time;
	time_t current_t;

	strcpy((char*)buff,"Durées d'arrosage par programme:\n");

	now(&current_time);
	current_t = mktime(&current_time);
	for( int i=0; i<4; i++ )
	{
		uint8_t diffD = dayCycleRemaining( current_t, i );
		sprintf((char*)out,"%c: %d min.",'A'+i,programDuration[i]);
		strcat((char*)buff,(char*)out);
		if( diffD > 0 )
		{
			sprintf((char*)out,", dans %d jr\n",diffD);
			strcat((char*)buff,(char*)out);
		}
		else
			strcat((char*)buff,"\n");
	}

	strcat((char*)buff,"Séquences d'arrosage du jour:\n");
	int index = strlen((char*)buff);
	dailyWatering((char*)&buff[index]);

	int size = strlen((char*)buff);
	uint32_t crc = calcCRC32stm(buff, size, 0x04C11DB7, 0xffffffff, 1, 1, 1);
	sprintf((char*)out,"#d=%04d,%08lX",size,crc);
	sendData( (uint8_t*)out, strlen((char*)out) );
	// Calculate chunk number
	uint16_t nChunk = size / CHUNK_SIZE;
	uint16_t remain = size % CHUNK_SIZE;
	if( remain > 0 )
		nChunk += 1;
	else if( remain == 0)
		remain = CHUNK_SIZE;

	// Sending data
	for( int c=0; c < nChunk; c++ )
	{
		out[1] = c+1;
		if( c == nChunk - 1 )
		{
			out[0]=51;
			size = remain;
		}
		else
		{
			out[0]=50;
			size = CHUNK_SIZE;
		}
		strncpy((char*)&out[2],(char*)&buff[c*CHUNK_SIZE],size);
		out[size+2] = '\0';
		//APP_LOG(TS_OFF, VLEVEL_M, out);
		sendData( (uint8_t*)out, strlen((char*)out) );
		osDelay(50);
	}
}

void spyCANbus( char* data_buffer )
{
osMessageQId destQueue;

	destQueue = (osMessageQId)getQueueId(CAN_COMM);

	MSG_STRUCT *p_sendMsg = allocMemoryPoolMessage(); // receiver must free
	uint8_t state = toIntLen( data_buffer, 1 );
	p_sendMsg->opcode = (state == 1 ? CAN_ECHO : CAN_NOECHO);
	p_sendMsg->lParam[0] = 0;
	if( data_buffer[1] == ',' ) // filter is active
	{
		uint16_t address = toIntLen( data_buffer, 3);
		p_sendMsg->lParam[0] = address;
	}
	osMessagePut(destQueue, (uint32_t)p_sendMsg, osWaitForever);
}

void requestStateMachine()
{
	updateFSM( finiteStateMachine );
}

static void sendCommandToCAN( CommandOpcode opcode, uint16_t param[6] )
{
osMessageQId destQueue;

	destQueue = (osMessageQId)getQueueId(CAN_COMM);

	MSG_STRUCT *p_sendMsg = allocMemoryPoolMessage(); // receiver must free
	p_sendMsg->opcode = opcode;
	memcpy(p_sendMsg->lParam,param,12);

	osMessagePut(destQueue, (uint32_t)p_sendMsg, osWaitForever);
}

static void sendCommandToSystem( CommandOpcode opcode, uint16_t param[6] )
{
osMessageQId destQueue;

	destQueue = (osMessageQId)getQueueId(AUTOMATION);

	MSG_STRUCT *p_sendMsg = allocMemoryPoolMessage(); // receiver must free
	p_sendMsg->changeState = UPDATE_SETTINGS;
	p_sendMsg->opcode = opcode;
	memcpy(p_sendMsg->lParam,param,12);

	osMessagePut(destQueue, (uint32_t)p_sendMsg, osWaitForever);
}

void setAuxOutputFeature(char* data_buffer)
{
	uint16_t p[6] = {0};

	p[0] = toIntLen( data_buffer, 1);		// Output no
	p[1] = toIntLen( &data_buffer[2], 1);	// autorun disabled/enabled
	p[2] = toIntLen( &data_buffer[4], 1);	// ActuatorAutoRunning (0..4)
	p[3] = toIntLen( &data_buffer[6], 3);	// Low limit
	p[4] = toIntLen( &data_buffer[10], 3);	// High limit
	sendCommandToSystem( OP_FEATURE, p );
}

void setModuleCalibration(char* data_buffer, int nParam )
{
	calib_buffer[0] = toIntLen( data_buffer, 4);
	calib_buffer[1] = toIntLen( &data_buffer[5], 1);
	if( calib_buffer[1] > 3)
	{
		int len = strlen( data_buffer ) - 7;
		int index = calib_buffer[1] - 4;
		calib_buffer[2+index] = toIntLen( &data_buffer[7], len );
		if( index < (nParam-1) )
			return;
	}
	sendCommandToCAN( OP_CAN, calib_buffer );
	memset(calib_buffer,0,12);
}

static void runCycle()
{
	sendCommandToAutomation( ALL_OUTPUTS_OFF, Z_RESTART );
}

void systemReset()
{
	sendCommandToAutomation( ALL_OUTPUTS_OFF, Z_ALL_OFF );
	HAL_NVIC_DisableIRQ(EXTI0_IRQn);
	osDelay(1500);
	HAL_NVIC_EnableIRQ(EXTI0_IRQn);
	osDelay(1500);
	runCycle();
}

/**
 * @brief  This function is used to send data related to the sample service
 *         (to be sent over the air to the remote board).
 * @param  data_buffer : pointer to data to be sent
 * @param  Nb_bytes : number of bytes to send
 * @retval None
 */
void sendData( uint8_t* data_buffer, uint8_t Nb_bytes )
{
	aci_gatt_update_char_value(sampleServHandle, TXCharHandle, 0, Nb_bytes, data_buffer);
}

void sendBLEerror( uint16_t errno )
{
	aci_gatt_update_char_value(sampleServHandle, ErrCharHandle, 0, 2, &errno);
}

void updateFSM()
{
	aci_gatt_update_char_value(sampleServHandle, StateCharHandle, 0, 1, &finiteStateMachine);
}

void updateReg75HC955()
{
char buff[3] = {'#', 'O', 0 };

	buff[2]= outputs74HC955 & 0x00FF;
	aci_gatt_update_char_value(sampleServHandle, RXCharHandle, 0, 3, buff);
}

/**
 * @brief  This function is called when an attribute gets modified
 * @param  handle : handle of the attribute
 * @param  data_length : size of the modified attribute data
 * @param  att_data : pointer to the modified attribute data
 * @retval None
 */
void Attribute_Modified_CB(uint16_t handle, uint8_t data_length, uint8_t *att_data)
{
	if(handle == RXCharHandle + 1)
	{
		readATcommand(data_length, att_data );
	}
	else if (handle == TXCharHandle + 2)
	{
		if(att_data[0] == 0x01)
			notification_tx_enabled = TRUE;
	}
	else if (handle == StateCharHandle + 1)
	{
		finiteStateMachine.stm = att_data[0];
	}
	else if (handle == ErrCharHandle + 2)
	{
		if(att_data[0] == 0x01)
			notification_err_enabled = TRUE;
	}
}

/**
 * @brief  This function is called when there is a LE Connection Complete event.
 * @param  addr : Address of peer device
 * @param  handle : Connection handle
 * @retval None
 */
void GAP_ConnectionComplete_CB(uint8_t addr[6], uint16_t handle)
{
  connected = TRUE;
  connection_handle = handle;

  APP_LOG(TS_OFF, VLEVEL_M, "Connected to device:");
  for(int i = 5; i > 0; i--){
    APP_LOG(TS_OFF, VLEVEL_M, "%02X-", addr[i]);
  }
  APP_LOG(TS_OFF, VLEVEL_M, "%02X\r\n", addr[0]);
}

/**
 * @brief  This function is called when the peer device get disconnected.
 * @param  None
 * @retval None
 */
void GAP_DisconnectionComplete_CB(void)
{
  connected = FALSE;

  APP_LOG(TS_OFF, VLEVEL_M, "Disconnected\r\n");
  if( finiteStateMachine.stm == APERIODIC )
	  safeStop();
  /* Make the device connectable again. */
  set_connectable = TRUE;
  notification_tx_enabled = FALSE;
  notification_fsm_enabled = FALSE;
  notification_err_enabled = FALSE;
}

/**
 * @brief  This function is called when there is a notification from the server.
 * @param  attr_handle Handle of the attribute
 * @param  attr_len    Length of attribute value in the notification
 * @param  attr_value  Attribute value in the notification
 * @retval None
 */
void GATT_Notification_CB(uint16_t attr_handle, uint8_t attr_len, uint8_t *attr_value)
{
  if (attr_handle == tx_handle+1) {
    receiveData(attr_value, attr_len);
  }
}

/**
 * @brief  This function is called when a read request or read blob request is received.
 * @param  attr_handle Handle of the attribute
 * @param  attr_len    Length of attribute value in the read
 * @param  attr_value  Attribute value in the read
 * @retval None
 */
void GATT_Read_Permit_Req_CB(uint16_t attr_handle, uint8_t attr_len, uint8_t *attr_value)
{
  if (attr_handle == date_handle+1)
  {
  }
  else if (attr_handle == state_handle+1)
  {
	  finiteStateMachine.stm = attr_value[0];
  }
  else if (attr_handle == err_handle+1)
  {
  }
}

/**
 * @brief  This function is called whenever there is an ACI event to be processed.
 * @note   Inside this function each event must be identified and correctly
 *         parsed.
 * @param  pData  Pointer to the ACI packet
 * @retval None
 */
void user_notify(void * pData)
{
  hci_uart_pckt *hci_pckt = pData;
  uint8_t dummy;
  /* obtain event packet */
  hci_event_pckt *event_pckt = (hci_event_pckt*)hci_pckt->data;

  if(hci_pckt->type != HCI_EVENT_PKT)
    return;

  switch(event_pckt->evt){

  case EVT_DISCONN_COMPLETE:
    {
      GAP_DisconnectionComplete_CB();
    }
    break;

  case EVT_LE_META_EVENT:
    {
      evt_le_meta_event *evt = (void *)event_pckt->data;

      switch(evt->subevent){
      case EVT_LE_CONN_COMPLETE:
        {
          evt_le_connection_complete *cc = (void *)evt->data;
          GAP_ConnectionComplete_CB(cc->peer_bdaddr, cc->handle);
        }
        break;
      case EVT_LE_CONN_UPDATE_COMPLETE:
		{
		  //evt_le_connection_update_complete *cc = (void *)evt->data;
		}
    	break;
      }
    }
    break;

  case EVT_VENDOR:
    {
      evt_blue_aci *blue_evt = (void*)event_pckt->data;
      //APP_LOG(TS_ON, VLEVEL_M,"EVT_VENDOR %d\r\n", blue_evt->ecode);
      switch(blue_evt->ecode){

      case EVT_BLUE_GATT_ATTRIBUTE_MODIFIED:
        {
          //APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_ATTRIBUTE_MODIFIED\r\n");
          evt_gatt_attr_modified_IDB05A1 *evt = (evt_gatt_attr_modified_IDB05A1*)blue_evt->data;
          Attribute_Modified_CB(evt->attr_handle, evt->data_length, evt->att_data);
        }
        break;
      case EVT_BLUE_GATT_NOTIFICATION:
        {
          APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_NOTIFICATION\r\n");
          evt_gatt_attr_notification *evt = (evt_gatt_attr_notification*)blue_evt->data;
          GATT_Notification_CB(evt->attr_handle, evt->event_data_length - 2, evt->attr_value);
        }
        break;

      case EVT_BLUE_GATT_READ_PERMIT_REQ:
		{
			//APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_READ_PERMIT_REQ\r\n");
			evt_gatt_read_permit_req *evt = (evt_gatt_read_permit_req*)blue_evt->data;
			//GATT_Read_Permit_Req_CB(evt->attr_handle, evt->event_data_length - 2, evt->attribute_value);
			if(evt->attr_handle == DateCharHandle + 1)
			{
				uint8_t buff[13];

				uint32_t epoch = get_EpochTime();	// UTC time
				epoch -= (Flash.settings.timeZoneOffset*3600);

				initDaylyWateringList();
				uint8_t step = findSprinklingStep();
				buff[11] = findDayDelay(step+1);
				uint8_t ret = (uint8_t)findValvesStepState(step);

				strcpy((char*)buff,"#?");
				HOST_TO_LE_32(&buff[2],epoch);
				buff[7] = step;
				buff[8] = ret;
				buff[6] = get_alarmA_time( &buff[9], &buff[10], &dummy );
			    buff[12] = getCaseTemperature();

				aci_gatt_update_char_value(sampleServHandle, DateCharHandle,
				                                            0, /* charValOffset */
				                                            13, /* charValueLen */
															buff);
				updateReg75HC955();
				(void)aci_gatt_allow_read(evt->conn_handle);

			}
		}
    	break;

      case EVT_BLUE_GATT_INDICATION:
      	APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_INDICATION\r\n");
        break;

      case EVT_BLUE_GATT_DISC_READ_CHAR_BY_UUID_RESP:
    	APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_DISC_READ_CHAR_BY_UUID_RESP\r\n");
        break;

      case EVT_BLUE_GAP_PASS_KEY_REQUEST:
        	APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GAP_PASS_KEY_REQUEST\r\n");
    	break;

      case EVT_BLUE_GAP_PAIRING_CMPLT:
		{
			//evt_gap_pairing_cmplt *evt = (evt_gap_pairing_cmplt*)blue_evt->data;
		}
        break;

      case EVT_BLUE_GATT_PROCEDURE_COMPLETE:
    	  APP_LOG(TS_ON, VLEVEL_M,"EVT_BLUE_GATT_PROCEDURE_COMPLETE\r\n");
        break;
      }
    }
    break;
  }
}
/**
 * @}
 */

void readATcommand( uint8_t data_length, uint8_t *att_data )
{
char command[30];

	if( !strncmp((char*)att_data,"AT#",3) )
	{
		strncpy(command,(const char*)&att_data[3],data_length-3);
		command[data_length-2] = '\0';
		if( !strncmp( command, "UT=", 3) ) // Setting date and time, AT#UT=1644649142 (UnixTime format)
		{
			receiveDateTime(&command[3]);
		}
		else if( !strncmp( command, "DA=", 3) ) // Update actuator register, AT#DA=005,080 means switch on only valve 1 and 3 at 80 pump rate
		{
			driveManyValves(&command[3]);
		}
		else if( !strncmp( command, "ES", 2) ) // Emergency stop
		{
			emergencyStop();
		}
		else if( !strncmp( command, "RS", 2) ) // Reset
		{
			systemReset();
		}
		else if( !strncmp( command, "SM", 2) ) // Request State Machine to central
		{
			requestStateMachine();
		}
		else if( !strncmp( command, "RC", 2) ) // Run current cycle
		{
			runCycle();
		}
		else if( !strncmp( command, "WT", 2) ) // Get watering timing
		{
			getWateringStats();
		}
		else if( !strncmp( command, "CB=", 3) ) // Spy CAN-bus
		{
			spyCANbus(&command[3]); // can be followed by filter like, AT#CB=1,00784
		}
		else if( !strncmp( command, "AO=", 3) ) // Set/Unset auxilary output, AT#AO=3,1,1,012,080 means AUX output 3, autostart, when ref between
			// range low and high, with low limit at 12 and high at 80
		{
			setAuxOutputFeature(&command[3]);
		}
		else if( !strncmp( command, "RQ=", 3) ) // request to a module, AT#RQ=0272,4,3400 means request calibration(3+) to module 0x110 with one param (3400)
		{
			setModuleCalibration(&command[3],2);
		}
		else if( !strncmp( command, "DS", 2) ) // Download settings to BLE client
		{
			downloadingSettings();
		}
		else if( !strncmp( command, "US=", 3) ) // Upload settings from BLE client
		{
			uploadingSettings(&command[3]);
		}
		else if( !strncmp( command, "RD", 2) ) // Set default settings
		{
			setDefaultSettings();
		}
		else
			receiveData(att_data, data_length);
	}
	else
		receiveData(att_data, data_length);
}
/**
 * @}
 */

/**
 * @}
 */

/**
 * @}
 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
