#include "Module_OcppBackend20.h"

void CallErrorHandler(char *id, char *errorCode, char *errorDescription,char *payload);
int CallHandler(char *uuid, char *str1,char *payload);
void CallResultHandler(char *str1,char *payload, int gun_index);
extern void CheckTransactionPacket(char *uuid);

typedef void (*FunCallErrorPtr)(char *id, char *errorCode, char *errorDescription,char *payload);
typedef void (*FunPtr)(char *payload, int gun_index);
typedef int (*FunCallPtr)(char *uuid, char *payload);
typedef enum boolean { FALSE, TRUE } BOOL;


static char *requestNames[] = { "CancelReservation",
								"CertificateSigned",
								"ChangeAvailability",
								"ClearCache",
								"ClearChargingProfile",
								"ClearDisplayMessage",
								"ClearVariableMonitoring",
								"CostUpdated",
								"CustomerInformation",
								"DataTransfer",
								"DeleteCertificate",
								"GetBaseReport",
								"GetChargingProfiles",
								"GetCompositeSchedule",
								"GetDisplayMessages",
								"GetInstalledCertificateIds",
								"GetLocalListVersion",
								"GetLog",
								"GetMonitoringReport",
								"GetReport",
								"GetTransactionStatus",
								"GetVariables",
								"InstallCertificate",
								"PublishFirmware",
								"RemoteStartTransaction",
								"RemoteStopTransaction",
								"ReserveNow",
								"Reset",
								"SendLocalList",
								"SetChargingProfile",
								"SetDisplayMessage",
								"SetMonitoringBase",
								"SetMonitoringLevel",
								"SetNetworkProfile",
								"SetVariableMonitoring",
								"SetVariables",
								"TriggerMessage",
								"UnlockConnector",
								"UnpublishFirmware",
								"UpdateFirmware"};

static char *responseNames[] = {"Authorize",
								"BootNotification",
								"ClearedChargingLimit",
								"DataTransfer",
								"FirmwareStatusNotification",
								"Get15118EVCertificate",
								"GetCertificateStatus",
								"Heartbeat",
								"LogStatusNotification",
								"MeterValues",
								"NotifyChargingLimit",
								"NotifyCustomerInformation",
								"NotifyDisplayMessages",
								"NotifyEVChargingNeeds",
								"NotifyEVChargingSchedule",
								"NotifyEvent",
								"NotifyMonitoringReport",
								"NotifyReport",
								"PublishFirmwareStatusNotification",
								"ReportChargingProfiles",
								"ReservationStatusUpdate",
								"SecurityEventNotification",
								"SignCertificate",
								"StatusNotification",
								"TransactionEven" };

static FunPtr funs[] = {handleAuthorizeResponse,
						handleBootNotificationResponse,
						handleClearedChargingLimitResponse,
						handleDataTransferResponse,
						handleFirmwareStatusNotificationResponse,
						handleGet15118EVCertificateResponse,
						hanldeGetCertificateStatusResponse,
						handleHeartbeatResponse,
						hanldeLogStatusNotificationResponse,
						handleMeterValuesResponse,
						handleNotifyChargingLimitResponse,
						hanldeNotifyCustomerInformationResponse,
						hanldeNotifyDisplayMessagesResponse,
						hanldeNotifyEVChargingNeedsResponse,
						hanldeNotifyEVChargingScheduleResponse,
						hanldeNotifyEventResponse,
						hanldeNotifyMonitoringReportResponse,
						hanldeNotifyReportResponse,
						hanldePublishFirmwareStatusNotificationResponse,
						hanldeReportChargingProfilesResponse,
						hanldeReservationStatusUpdateResponse,
						hanldeSecurityEventNotificationResponse,
						hanldeSignCertificateResponse,
						handleStatusNotificationResponse,
						hanldeTransactionEvenResponse};

static FunCallPtr funcalls[] = {handleCancelReservationRequest,
								handleCertificateSignedRequest,
								handleChangeAvailabilityRequest,
								handleClearCacheRequest,
								handleClearChargingProfileRequest,
								handleClearDisplayMessageRequest,
								handleClearVariableMonitoringRequest,
								handleCostUpdatedRequest,
								handleCustomerInformationRequest,
								handleDataTransferRequest,
								handleDeleteCertificateRequest,
								handleGetBaseReportRequest,
								handleGetChargingProfilesRequest,
								handleGetCompositeScheduleRequest,
								handleGetDisplayMessagesRequest,
								handleGetInstalledCertificateIdsRequest,
								handleGetLocalListVersionRequest,
								handleGetLogRequest,
								handleGetMonitoringReportRequest,
								handleGetReportRequest,
								handleGetTransactionStatusRequest,
								handleGetVariablesRequest,
								handleInstallCertificateRequest,
								handlePublishFirmwareRequest,
								handleRemoteStartTransactionRequest,
								handleRemoteStopTransactionRequest,
								handleReserveNowRequest,
								handleResetRequest,
								handleSendLocalListRequest,
								handleSetChargingProfileRequest,
								handleSetDisplayMessageRequest,
								handleSetMonitoringBaseRequest,
								handleSetMonitoringLevelRequest,
								handleSetNetworkProfileRequest,
								handleSetVariableMonitoringRequest,
								handleSetVariablesRequest,
								handleTriggerMessageRequest,
								handleUnlockConnectorRequest,
								handleUnpublishFirmwareRequest,
								handleUpdateFirmwareRequest};

static FunCallErrorPtr funcallerror[] = {	handleError	};

//==========================================

// Receive Message routine

//==========================================
void ReceivedMessage(void *in, size_t len)
{
	//DEBUG_INFO("ReceivedMessage\n");
	char tempin[65536];
	int MsgType = 0;
	char UniqueId[37],Action[33],Payload[64824],ErrorCode[129],ErrorDescription[513];
	char *arr[2]= {};
	int gun_index = 0;
	const char *del = ",";
	char *substr = NULL;
	int count = 0;
	int i = 0;
	char key_value[VALUE_MAX_LENGTH];

	//parsing received message and do something
	memset(key_value, 0, sizeof key_value);
	memset(UniqueId, 0, sizeof UniqueId);
	memset(Action, 0, sizeof Action);
	memset(Payload, 0, sizeof Payload);
	memset(ErrorCode, 0, sizeof ErrorCode);
	memset(ErrorDescription, 0, sizeof ErrorDescription);
	memset(tempin, 0, 1024*4);
	strcpy(tempin, (const char *)in);
	memset( (void *)in, 0, sizeof(char)*len );

	if(tempin[0] != '\0')
	{
		if(strcmp((const char *)tempin,"[ ]") == 0)
		{
			DEBUG_WARN("Message is empty array.\n");
			return;
		}

		json_object *obj = NULL;
		obj = json_tokener_parse(tempin);
		if(!is_error(obj))
		{
			MsgType = json_object_get_int(json_object_array_get_idx(obj, 0));
			sprintf(UniqueId, "%s", json_object_get_string(json_object_array_get_idx(obj, 1)));

			if((MsgType != 2) && (MsgType != 3) && (MsgType != 4) )
			{
				DEBUG_WARN("Message type not valid.\n");
				return;
			}

			if(UniqueId[0] == '\0')
			{
				DEBUG_WARN("Message unique id is null.\n");
				return;
			}

			CheckTransactionPacket(UniqueId);

			switch (MsgType)
			{
				case MESSAGE_TYPE_CALL:
					sprintf(Action, "%s", json_object_get_string(json_object_array_get_idx(obj, 2)));
					sprintf(Payload, "%s", json_object_to_json_string_ext(json_object_array_get_idx(obj, 3), JSON_C_TO_STRING_PLAIN));
					CallHandler(UniqueId,Action,Payload);
					break;

				case MESSAGE_TYPE_CALLRESULT:
					sprintf(Payload, "%s", json_object_to_json_string_ext(json_object_array_get_idx(obj, 2), JSON_C_TO_STRING_PLAIN));
					if(hashmap_operation(HASH_OP_GET, UniqueId, key_value) == TRUE)
					{


						hashmap_operation(HASH_OP_REMOVE, UniqueId, key_value);
						char * const testdup  = strdup(key_value);

						substr = strtok(testdup, del);
						while (substr != NULL)
						{
							arr[count] = substr;
							count++;
							substr = strtok(NULL, del);
						}

						i=0;
						sprintf(Action, "%s", *(arr+i++));
						gun_index = atoi(*(arr+i++));
						CallResultHandler(Action, Payload, gun_index);

						free(testdup);
					}
					break;

			   case MESSAGE_TYPE_CALLERROR:
				   sprintf(ErrorCode, "%s", json_object_get_string(json_object_array_get_idx(obj, 2)));
				   sprintf(ErrorDescription, "%s", json_object_get_string(json_object_array_get_idx(obj, 3)));
				   sprintf(Payload, "%s", json_object_to_json_string_ext(json_object_array_get_idx(obj, 4), JSON_C_TO_STRING_PLAIN));

				   if(hashmap_operation(HASH_OP_GET, UniqueId, key_value) == TRUE)
				   {
					   hashmap_operation(HASH_OP_REMOVE, UniqueId, key_value);
					   sprintf(Action, "%s", key_value);
					   CallErrorHandler(UniqueId,ErrorCode, ErrorDescription, "");
				   }

				   break;

				default:
					break;
			}
		}
		else
		{
			DEBUG_WARN("Message is invalid JSON format.\n");
		}
		json_object_put(obj);
	}
	else
	{
		DEBUG_WARN("Message is null. can't parse message.\n");
	}
}

int CallHandler(char *uuid, char *str1,char *payload)
{
	static int CallHandlerNumber = 0;
    static int CallHandlerIndex = 0;
    int (*callfptr)(char *uuid,char *payload);
    //DEBUG_INFO("enter CallHandler\n");
	CallHandlerNumber = sizeof(requestNames)/sizeof(requestNames[0]);
	for(int i= 0; i < CallHandlerNumber ; i ++ )
	{
		if(strcmp(requestNames[i],str1) == 0)
		{
			CallHandlerIndex = i ;
			break;
		}
	}

	callfptr = NULL;
	callfptr = funcalls[CallHandlerIndex];

	if(callfptr == NULL)
	{
		//printf("callfptr is null\n");
	}

	if ( callfptr )
	{
		//printf("exec CallHandler ... \n");
		callfptr(uuid, payload);
		callfptr = NULL;
		return PASS;
	}

	callfptr = NULL;
	 return FAIL;
}

void CallResultHandler(char *str1, char *payload, int gun_index)
{
	static int CallResultHandlerNumber = 0;
	static int CallResultHandlerIndex = 0;
	void (*callResultfptr)(char *payload, int gun_index );
	//printf("enter CallResultHandler\n");
	CallResultHandlerNumber = sizeof(responseNames)/sizeof(responseNames[0]);
	for(int i= 0; i < CallResultHandlerNumber ; i ++ )
	{
		if(strcmp(responseNames[i],str1) == 0)
		{
			CallResultHandlerIndex = i ;
			break;
		}
	}

	callResultfptr = NULL;
	callResultfptr = funs[CallResultHandlerIndex];

	if(callResultfptr == NULL)
	{
		//printf("callResultfptr is null\n");
	}

	if ( callResultfptr )
	{
		callResultfptr(payload, gun_index);
	}

	callResultfptr = NULL;
}

void CallErrorHandler(char *id, char *errorCode, char *errorDescription,char *payload)
{
	void (*callErrorfptr)(char *id, char *errorCode, char *errorDescription,char *payload );
	callErrorfptr = NULL;
	callErrorfptr = funcallerror[0];

	//printf("CallErrorHandler \n");

	if(callErrorfptr == NULL)
	{
		printf("callErrorfptr is null\n");
	}

	if ( callErrorfptr )
	{
		//printf("callErrorfptr is not null\n");

		callErrorfptr(id, errorCode, errorDescription, payload);
	}

	callErrorfptr = NULL;
}