//When a connection changes to encrypted
void ConnectionManager::ConnectionEncryptedHandler(ble_evt_t* bleEvent)
{
	ConnectionManager* cm = ConnectionManager::getInstance();
	Connection* c = cm->GetConnectionFromHandle(bleEvent->evt.gap_evt.conn_handle);

	logt("CM", "Connection id %u is now encrypted", c->connectionId);
	c->encryptionState = Connection::EncryptionState::ENCRYPTED;

	//We are peripheral
	if(c->direction == Connection::CONNECTION_DIRECTION_IN)
	{
		if(!cm->doHandshake){
			c->connectionState = Connection::ConnectionState::HANDSHAKE_DONE;
		}
	}
	//We are central
	else if(c->direction == Connection::CONNECTION_DIRECTION_OUT)
	{
		if(cm->doHandshake)
		{
			c->StartHandshake();
		}
		//If the handshake is disabled, we just set the variable
		else
		{
			c->connectionState = Connection::ConnectionState::HANDSHAKE_DONE;
		}
	}
}
//Is called whenever a connection had been established and is now disconnected
//due to a timeout, deliberate disconnection by the localhost, remote, etc,...
void ConnectionManager::DisconnectionHandler(ble_evt_t* bleEvent)
{

	ConnectionManager* cm = ConnectionManager::getInstance();
	Connection* connection = cm->GetConnectionFromHandle(bleEvent->evt.gap_evt.conn_handle);

	if(connection == NULL) return;

	//Save disconnction reason
	connection->disconnectionReason = bleEvent->evt.gap_evt.params.disconnected.reason;

	//LOG disconnection reason
	Logger::getInstance().logError(Logger::errorTypes::HCI_ERROR, bleEvent->evt.gap_evt.params.disconnected.reason, cm->node->appTimerMs);

	logt("CM", "Connection %u to %u DISCONNECTED", connection->connectionId, connection->partnerId);

	//Check if the connection should be sustained (If it's been handshaked for more than 10 seconds and ran into a timeout)
	if(
			cm->node->appTimerMs - connection->connectionHandshakedTimestamp > 10 * 1000
			&& bleEvent->evt.gap_evt.params.disconnected.reason == BLE_HCI_CONNECTION_TIMEOUT
			&& cm->node->currentDiscoveryState != discoveryState::HANDSHAKE
			&& cm->node->currentDiscoveryState != discoveryState::REESTABLISHING_CONNECTION
	){
		logt("CM", "Connection should be sustained");

		//TODO: implement

		/*connection->connectionState = Connection::REESTABLISHING;

		cm->node->ChangeState(discoveryState::REESTABLISHING_CONNECTION);

		//We assume the same role as before the connection loss
		if(connection->direction == Connection::ConnectionDirection::CONNECTION_DIRECTION_IN){
			//We advertise
			AdvertisingController::SetAdvertisingState(advState::ADV_STATE_HIGH);
		} else {
			//We try to initiate the connection
			GAPController::connectToPeripheral(&connection->partnerAddress, Config->meshExtendedConnectionTimeout);

		}*/

	}


	if (connection->direction == Connection::CONNECTION_DIRECTION_IN) cm->freeInConnections++;
	else cm->freeOutConnections++;

	//If this was the pending connection, we clear it
	if(cm->pendingConnection == connection) cm->pendingConnection = NULL;

	//Notify the connection itself
	connection->DisconnectionHandler(bleEvent);

	//Notify the callback of the disconnection (The Node probably)
	cm->connectionManagerCallback->DisconnectionHandler(bleEvent);

	//Reset connection variables
	connection->ResetValues();
}
//When the mesh handle has been discovered
void ConnectionManager::handleDiscoveredCallback(u16 connectionHandle, u16 characteristicHandle)
{
	ConnectionManager* cm = ConnectionManager::getInstance();

	Connection* connection = cm->GetConnectionFromHandle(connectionHandle);
	if (connection != NULL)
	{
		connection->writeCharacteristicHandle = characteristicHandle;

		if(cm->doHandshake) connection->StartHandshake();
		else {
			cm->connectionManagerCallback->ConnectionSuccessfulHandler(NULL);
		}
	}
}
void ConnectionManager::dataTransmittedCallback(ble_evt_t* bleEvent)
{

	ConnectionManager* cm = ConnectionManager::getInstance();
	//There are two types of events that trigger a dataTransmittedCallback
	//A TX complete event frees a number of transmit buffers
	//These are used for all connections
	if(bleEvent->header.evt_id == BLE_EVT_TX_COMPLETE)
	{

		logt("CONN_DATA", "write_CMD complete (n=%d)", bleEvent->evt.common_evt.params.tx_complete.count);

		//This connection has just been given back some transmit buffers
		Connection* connection = cm->GetConnectionFromHandle(bleEvent->evt.common_evt.conn_handle);
		connection->unreliableBuffersFree += bleEvent->evt.common_evt.params.tx_complete.count;

		connection->sentUnreliable++;

		//Next, we should continue sending packets if there are any
		if(cm->GetPendingPackets()) cm->fillTransmitBuffers();

	}
	//The EVT_WRITE_RSP comes after a WRITE_REQ and notifies that a buffer
	//for one specific connection has been cleared
	else if (bleEvent->header.evt_id == BLE_GATTC_EVT_WRITE_RSP)
	{
		if(bleEvent->evt.gattc_evt.gatt_status != BLE_GATT_STATUS_SUCCESS)
		{
			logt("ERROR", "GATT status problem %d %s", bleEvent->evt.gattc_evt.gatt_status, Logger::getGattStatusErrorString(bleEvent->evt.gattc_evt.gatt_status));

			//TODO: Error handling, but there really shouldn't be an error....;-)
			//FIXME: Handle possible gatt status codes

		}
		else
		{
			logt("CONN_DATA", "write_REQ complete");
			Connection* connection = cm->GetConnectionFromHandle(bleEvent->evt.gattc_evt.conn_handle);

			//Connection could have been disconneced
			if(connection == NULL) return;

			connection->sentReliable++;

			//Check what type of Packet has just been sent
			connPacketHeader* packetHeader = (connPacketHeader*)connection->lastSentPacket;

			if(packetHeader->messageType == MESSAGE_TYPE_CLUSTER_INFO_UPDATE){
				//Nothing to do
			} else {

				logt("CONN_DATA", "Header was type %d hasMoreParts %d", packetHeader->messageType, packetHeader->hasMoreParts);

				//Check if the packet has more parts
				if(packetHeader->hasMoreParts == 0){
					//Packet was either not split at all or is completely sent
					connection->packetSendPosition = 0;

					//Check if this was the end of a handshake, if yes, mark handshake as completed
					if(packetHeader->messageType == MESSAGE_TYPE_CLUSTER_ACK_2)
					{
						//Notify Node of handshakeDone
						cm->node->HandshakeDoneHandler(connection, true);
					}

					//Discard the last packet because it was now successfully sent
					connection->packetSendQueue->DiscardNext();
				} else {
					//Update packet send position if we have more data
					connection->packetSendPosition += MAX_DATA_SIZE_PER_WRITE - SIZEOF_CONN_PACKET_SPLIT_HEADER;
				}
			}

			connection->reliableBuffersFree += 1;


			//Now we continue sending packets
			if(cm->GetPendingPackets()) cm->fillTransmitBuffers();
		}
	}
}
void ConnectionManager::messageReceivedCallback(ble_evt_t* bleEvent)
{
	ConnectionManager* cm = ConnectionManager::getInstance();

	//Handles BLE_GATTS_EVT_WRITE


	//FIXME: must check for reassembly buffer size, if it is bigger, a stack overflow will occur

	Connection* connection = cm->GetConnectionFromHandle(bleEvent->evt.gatts_evt.conn_handle);
	if (connection != NULL)
	{
		//TODO: At this point we should check if the write was a valid operation for the mesh
		//Invalid actions should cause a disconnect
		/*if( bleEvent->evt.gatts_evt.params.write.handle != GATTController::getMeshWriteHandle() ){
			connection->Disconnect();
			logt("ERROR", "Non mesh device was disconnected");
		}*/


		connPacketHeader* packet = (connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data;

		//At first, some special treatment for out timestamp packet
		if(packet->messageType == MESSAGE_TYPE_UPDATE_TIMESTAMP)
		{
			//Set our time to the received timestamp and update the time when we've received this packet
			app_timer_cnt_get(&cm->node->globalTimeSetAt);
			cm->node->globalTime = ((connPacketUpdateTimestamp*)packet)->timestamp;

			logt("NODE", "time updated at:%u with timestamp:%u", cm->node->globalTimeSetAt, (u32)cm->node->globalTime);
		}

		u8 t = ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->messageType;

		if( t != 20 && t != 21 && t != 22 && t != 23 && t != 30 && t != 31 && t != 50 && t != 51 && t != 52 && t != 53 && t != 56 && t != 57 && t != 60 && t != 61 && t != 62 && t != 80 && t != 81){
			logt("ERROR", "WAAAAAAAAAAAAAHHHHH, WRONG DATAAAAAAAAAAAAAAAAA!!!!!!!!!");
		}

		//Print packet as hex
		char stringBuffer[100];
		Logger::getInstance().convertBufferToHexString(bleEvent->evt.gatts_evt.params.write.data, bleEvent->evt.gatts_evt.params.write.len, stringBuffer, 100);
		logt("CONN_DATA", "Received type %d, hasMore %d, length %d, reliable %d:", ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->messageType, ((connPacketHeader*)bleEvent->evt.gatts_evt.params.write.data)->hasMoreParts, bleEvent->evt.gatts_evt.params.write.len, bleEvent->evt.gatts_evt.params.write.op);
		logt("CONN_DATA", "%s", stringBuffer);

		//Check if we need to reassemble the packet
		if(connection->packetReassemblyPosition == 0 && packet->hasMoreParts == 0)
		{

			//Single packet, no more data
			connectionPacket p;
			p.connectionHandle = bleEvent->evt.gatts_evt.conn_handle;
			p.data = bleEvent->evt.gatts_evt.params.write.data;
			p.dataLength = bleEvent->evt.gatts_evt.params.write.len;
			p.reliable = bleEvent->evt.gatts_evt.params.write.op == BLE_GATTS_OP_WRITE_CMD ? false : true;

			connection->ReceivePacketHandler(&p);

		}
		//First of a multipart packet, still has more parts
		else if(connection->packetReassemblyPosition == 0 && packet->hasMoreParts)
		{
			//Save at correct position of
			memcpy(
					connection->packetReassemblyBuffer,
					bleEvent->evt.gatts_evt.params.write.data,
					bleEvent->evt.gatts_evt.params.write.len
				);

			connection->packetReassemblyPosition += bleEvent->evt.gatts_evt.params.write.len;

			//Do not notify anyone until packet is finished
			logt("CM", "Received first part of message");
		}
		//Multipart packet, intermediate or last frame
		else if(connection->packetReassemblyPosition != 0)
		{
			memcpy(
				connection->packetReassemblyBuffer + connection->packetReassemblyPosition,
				bleEvent->evt.gatts_evt.params.write.data + SIZEOF_CONN_PACKET_SPLIT_HEADER,
				bleEvent->evt.gatts_evt.params.write.len - SIZEOF_CONN_PACKET_SPLIT_HEADER
			);

			//Intermediate packet
			if(packet->hasMoreParts){
				connection->packetReassemblyPosition += bleEvent->evt.gatts_evt.params.write.len - SIZEOF_CONN_PACKET_SPLIT_HEADER;

				logt("CM", "Received middle part of message");

			//Final packet
			} else {
				logt("CM", "Received last part of message");

				//Notify connection
				connectionPacket p;
				p.connectionHandle = bleEvent->evt.gatts_evt.conn_handle;
				p.data = connection->packetReassemblyBuffer;
				p.dataLength = bleEvent->evt.gatts_evt.params.write.len + connection->packetReassemblyPosition - SIZEOF_CONN_PACKET_SPLIT_HEADER;
				p.reliable = bleEvent->evt.gatts_evt.params.write.op == BLE_GATTS_OP_WRITE_CMD ? false : true;

				//Reset the assembly buffer
				connection->packetReassemblyPosition = 0;

				connection->ReceivePacketHandler(&p);
			}
		}
	}
}