#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/termios.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <linux/sockios.h> 
#include <linux/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h> 
#include <sys/timeb.h> 
#include <libwebsockets.h>
#include <lws_config.h>
#include "define.h"

#define Debug

struct SysConfigAndInfo			*ShmSysConfigAndInfo;
struct StatusCodeData 			*ShmStatusCodeData;
struct OCPP16Data 			*ShmOCPP16Data;


struct lws *wsi;
struct lws_context *context;

unsigned char *SendBuffer;
int SendBufLen=(1024*3);
int ConnectionEstablished=0;


#ifdef SystemLogMessage
int StoreLogMsg(unsigned char *DataString)
{
	unsigned char Buf[256];
	time_t CurrentTime;
	struct tm *tm;
			
	memset(Buf,0,sizeof(Buf));
	CurrentTime = time(NULL);
	tm=localtime(&CurrentTime);
	sprintf(Buf,"echo \"%04d.%02d.%02d %02d:%02d:%02d - %s\" >> /Storage/SystemLog/[%04d.%02d]SystemLog",
			tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,
			DataString,
			tm->tm_year+1900,tm->tm_mon+1);
	system(Buf);
	#ifdef Debug
	printf("%s \n",DataString);
	#endif
}		
#endif

int DiffTimeb(struct timeb ST, struct timeb ET)
{
	//return milli-second
	unsigned int StartTime,StopTime;
	
	StartTime=(unsigned int)ST.time;
	StopTime=(unsigned int)ET.time;
	return (StopTime-StartTime)*1000+ET.millitm-ST.millitm;
}	

/**************************************************************************************/
/**************************Init all share memory *********************************/
/**************************************************************************************/
int InitShareMemory()
{
	int MeterSMId;
	//creat ShmSysConfigAndInfo
	if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0) 
    	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmget ShmSysConfigAndInfo NG");
		#endif			
		return 0;
	}
    	else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1) 
    	{
    		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmat ShmSysConfigAndInfo NG");
		#endif		
		return 0;
   	 }
   	 //creat ShmStatusCodeData
   	 if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0) 
    	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmget ShmStatusCodeData NG");
		#endif		
		return 0;	
	}
    	else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1) 
    	{
    		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmat ShmStatusCodeData NG");
		#endif		
		return 0;
   	 }
   	 //creat ShmOCPP16Data
   	 if ((MeterSMId = shmget(ShmOcppModuleKey, sizeof(struct OCPP16Data),  0777)) < 0) 
    	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmget ShmOCPP16Data NG");
		#endif			
		return 0;
	}
    	else if ((ShmOCPP16Data = shmat(MeterSMId, NULL, 0)) == (void *) -1) 
    	{
    		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]InitShareMemory:shmat ShmOCPP16Data NG");
		#endif		
		return 0;
   	 }
   	memset(ShmOCPP16Data,0,sizeof(struct PsuData));
    	return 1;
}


int SendData(struct lws *wsi) 
{
    int n;
    int len;
    unsigned char *out = NULL;

    len = strlen(SendBuffer);
    out = (char *)malloc(sizeof(char)*(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING));
    memcpy (out + LWS_SEND_BUFFER_PRE_PADDING, SendBuffer, len );
   
    n = lws_write(wsi, out + LWS_SEND_BUFFER_PRE_PADDING, len, LWS_WRITE_TEXT);
   
    free(out);
    return n;
}

static int OCPP16Callback(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
{	

	#ifdef Debug
	printf("OCPP16Callback:reason=%d\n",reason);
	#endif
	switch (reason) 
	{
		case LWS_CALLBACK_CLIENT_ESTABLISHED://3
			#ifdef SystemLogMessage	
			StoreLogMsg("[Ocpp16]OCPP16Callback:LWS_CALLBACK_CLIENT_ESTABLISHED");
			#endif		
			//connected
			ConnectionEstablished=1;
			break;
		case LWS_CALLBACK_CLIENT_CONNECTION_ERROR://1
			#ifdef Debug
			printf("OCPP16Callback:LWS_CALLBACK_CLIENT_CONNECTION_ERROR:%s\n",in);
			#endif
			#ifdef SystemLogMessage	
			StoreLogMsg("[Ocpp16]OCPP16Callback:LWS_CALLBACK_CLIENT_CONNECTION_ERROR");
			#endif		
			//disconnected
			ConnectionEstablished=0;
			break;
		case LWS_CALLBACK_CLOSED://4
			#ifdef SystemLogMessage	
			StoreLogMsg("[Ocpp16]OCPP16Callback:LWS_CALLBACK_CLOSED");
			#endif
			//disconnected
			ConnectionEstablished=0;
			break;
		case LWS_CALLBACK_CLIENT_WRITEABLE://10
			//if(need to send message and its relevant data already store into SendBuffer)
				SendData(wsi);
			break;
		case LWS_CALLBACK_CLIENT_RECEIVE://8
			((char *)in)[len] = '\0';
			#ifdef Debug
			printf("OCPP16Callback:LWS_CALLBACK_CLIENT_RECEIVE : rx %d '%s'\n", (int)len, (char *)in);
			#endif
			//parsing received message and do something
			break;
		default:
			break;
	}

	return 0;
}

static struct lws_protocols protocols[] = {

	{
		"ocpp1.6",		
		OCPP16Callback,
		10240,	
		10240,
	},
	{
		"ocpp1.6",		
		OCPP16Callback,
		10240,	
		10240,
	},
	{
		NULL, NULL, 0		/* End of list */
	}
};

int ConnectWsServer()
{	
	struct lws_context_creation_info ContextInfo;
	struct lws_client_connect_info ConnInfo;
	int use_ssl=0;
	
	if(context!=NULL)
		lws_context_destroy(context);
	memset(&ContextInfo, 0, sizeof(struct lws_context_creation_info));
	ContextInfo.port=CONTEXT_PORT_NO_LISTEN;
	ContextInfo.iface=NULL;
	ContextInfo.ssl_private_key_password=NULL; 
	ContextInfo.ssl_cert_filepath=NULL; 
	ContextInfo.ssl_private_key_filepath=NULL; 
	ContextInfo.ssl_ca_filepath="/root/cacert.pem"; 
	ContextInfo.ssl_cipher_list=NULL; //use default one
	ContextInfo.gid=-1;
	ContextInfo.uid=-1;
	//if(security connection)
	//	ContextInfo.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
	ContextInfo.protocols=protocols;
	ContextInfo.timeout_secs= 30;
	//if(ping pong enabled)
		//ContextInfo.ws_ping_pong_interval= 15;//0 for none, else interval in seconds 
	ContextInfo.ws_ping_pong_interval= 0;	
	context = lws_create_context(&ContextInfo);
	if (context == NULL) 
	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]ConnectWsServer: lws_create_context NG");
		#endif	
		return 0;
	}
	memset(&ConnInfo,0,sizeof(struct lws_client_connect_info));
	ConnInfo.context = context;

	// fill up below information
	//ConnInfo.address=ServerIpAddress;
	//ConnInfo.port = ServerSrcPort;
	//ConnInfo.path=ServerPath;
	//ConnInfo.host=ServerHost;
	//ConnInfo.origin=ServerOrigin;

	/* ws://192.168.1.1:9091/greenlots/ocpp*/
	ConnInfo.address = "192.168.1.1";
	ConnInfo.port = 9091;
	ConnInfo.path="/ocpp/T123456789";
	ConnInfo.host = "192.168.1.1:9091";
	ConnInfo.origin = "192.168.1.1:9091";	

	use_ssl = LCCSCF_USE_SSL | LCCSCF_ALLOW_SELFSIGNED | LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK;
	//if(security connection)
	//	ConnInfo.ssl_connection = use_ssl;
	//else
		ConnInfo.ssl_connection=0;

	ConnInfo.protocol = protocols[1].name;
	ConnInfo.ietf_version_or_minus_one=-1;
	wsi=lws_client_connect_via_info(&ConnInfo);						 
	if (!wsi) 
	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]ConnectWsServer: lws_client_connect_via_info NG");
		#endif	
		return 0;
	}
	return 1;
}

int main(int argc,char *argv[])
{
	unsigned int StartTime=0;
	/**************** Initialization **********/		
	/*if(InitShareMemory()==0)
	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]main:InitShareMemory NG");
		#endif		
		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory=1;
		}	
		sleep(5);
		return 0;
	}*/	
  	if((SendBuffer=malloc(SendBufLen))==NULL)
	{
		#ifdef SystemLogMessage	
		StoreLogMsg("[Ocpp16]main: malloc(SendBufLen) NG");
		#endif	
		sleep(5);
		return 0;	
	}	
	while(ConnectionEstablished==0)
	{
		if((time((time_t*)NULL)-StartTime)>=60)
		{
			#ifdef debug
			printf("[OCPP16:]main:Execute ConnectWsServer1\n");
			#endif
			ConnectWsServer();
			StartTime=time((time_t*)NULL);
		}	
		lws_service(context, 10000);//timeout_ms	
	}
	memset(SendBuffer,0,SendBufLen);
	sprintf(SendBuffer,"[2,\"%s\",\"%s\",{\"chargePointVendor\":\"%s\",\"chargePointModel\":\"%s\",\"chargePointSerialNumber\":\"%s\",\"chargeBoxSerialNumber\":\"%s\",\"firmwareVersion\":\"%s\",\"imsi\":\"%s\"}]",
					"11112222",
					"BootNotification",
					"CpVendor",
					"CpModel",
					"CpSN",
					"CbSN",
					"CpFwVersion",
					"CpImsi");
	lws_callback_on_writable(wsi);
	while(1)
	{
		//prcessing
		lws_service(context, 500);//500 timeout_ms 	
	};
	

	free(SendBuffer);
}