#include "Module_OcppBackend.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",
								"ChangeAvailability",
								"ChangeConfiguration",
								"ClearCache",
								"ClearChargingProfile",
								"DataTransfer",
								"GetCompositeSchedule",
								"GetConfiguration",
								"GetDiagnostics",
								"GetLocalListVersion",
								"RemoteStartTransaction",
								"RemoteStopTransaction",
								"ReserveNow",
								"Reset",
								"SendLocalList",
								"SetChargingProfile",
								"TriggerMessage",
								"UnlockConnector",
								"UpdateFirmware" };

static char *responseNames[] = {"Authorize",
								"BootNotification",
								"DataTransfer",
								"DiagnosticsStatusNotification",
								"FirmwareStatusNotification",
								"Heartbeat",
								"MeterValues",
								"StartTransaction",
								"StatusNotification",
								"StopTransaction" };

static FunPtr funs[] = {handleAuthorizeResponse,
						handleBootNotificationResponse,
						handleDataTransferResponse,
						handleDiagnosticsStatusNotificationResponse,
						handleFirmwareStatusNotificationResponse,
						handleHeartbeatResponse,
						handleMeterValuesResponse,
						handleStartTransactionResponse,
						handleStatusNotificationResponse,
						handleStopTransactionnResponse };

static FunCallPtr funcalls[] = {handleCancelReservationRequest,
								handleChangeAvailabilityRequest,
								handleChangeConfigurationRequest,
								handleClearCacheRequest,
								handleClearChargingProfileRequest,
								handleDataTransferRequest,
								handleGetCompositeScheduleRequest,
								handleGetConfigurationRequest,
								handleGetDiagnosticsRequest,
								handleGetLocalListVersionRequest,
								handleRemoteStartRequest,
								handleRemoteStopTransactionRequest,
								handleReserveNowTransactionRequest,
								handleResetRequest,
								handleSendLocalListRequest,
								handleSetChargingProfileRequest,
								handleTriggerMessageRequest,
								handleUnlockConnectorRequest,
								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);
	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)
	{}

	if ( callfptr )
	{
		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 );

	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)
	{}

	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];

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

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

	callErrorfptr = NULL;
}