/*
 * Module_InitUpgrade.c
 *
 *  Created on: 2021/01/29
 *      Author: foluswen
 */

#include 	<sys/time.h>
#include 	<sys/timeb.h>
#include    <sys/types.h>
#include    <sys/stat.h>
#include 	<sys/types.h>
#include 	<sys/ioctl.h>
#include 	<sys/socket.h>
#include 	<sys/ipc.h>
#include 	<sys/shm.h>
#include 	<sys/shm.h>
#include 	<sys/mman.h>
#include 	<linux/wireless.h>
#include 	<arpa/inet.h>
#include 	<netinet/in.h>

#include 	<unistd.h>
#include 	<stdarg.h>
#include    <stdio.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <fcntl.h>
#include    <termios.h>
#include    <errno.h>
#include 	<errno.h>
#include 	<string.h>
#include	<time.h>
#include	<ctype.h>
#include 	<ifaddrs.h>
#include 	<sys/types.h>
#include 	<sys/socket.h>
#include 	<netinet/in.h>
#include 	<netdb.h>
#include 	<error.h>
#include 	<signal.h>
#include	"define.h"

#define DEBUG_INFO(format, args...) StoreLogMsg("[%s:%d][%s][Info] "format, (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__), __LINE__, __FUNCTION__, ##args)
#define DEBUG_WARN(format, args...) StoreLogMsg("[%s:%d][%s][Warn] "format, (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__), __LINE__, __FUNCTION__, ##args)
#define DEBUG_ERROR(format, args...) StoreLogMsg("[%s:%d][%s][Error] "format, (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__), __LINE__, __FUNCTION__, ##args)

#define is_error(ptr) 				((unsigned long)ptr > (unsigned long)-4000L)
#define ARRAY_SIZE(A)				(sizeof(A) / sizeof(A[0]))
#define PASS						1
#define FAIL			   			-1
#define ON							1
#define OFF							0
#define YES							1
#define NO							0
#define true			    		1
#define false						0
#define MtdBlockSize				0x300000

struct SysConfigAndInfo		*ShmSysConfigAndInfo;
struct StatusCodeData 		*ShmStatusCodeData;


//==========================================
// Common routine
//==========================================
int StoreLogMsg(const char *fmt, ...)
{
	char Buf[4096+256];
	char buffer[4096];
	time_t CurrentTime;
	struct tm *tm;
	struct timeval tv;
	va_list args;

	va_start(args, fmt);
	int rc = vsnprintf(buffer, sizeof(buffer), fmt, args);
	va_end(args);

	memset(Buf,0,sizeof(Buf));
	CurrentTime = time((time_t*)NULL);
	tm=localtime(&CurrentTime);
	gettimeofday(&tv, NULL); // get microseconds, 10^-6


	sprintf(Buf,"echo -n \"[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s\" >> /Storage/SystemLog/[%04d.%02d]Module_InitUpgradeLog",
				tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec,
				buffer,
				tm->tm_year+1900,tm->tm_mon+1);

#ifdef SystemLogMessage
	system(Buf);
#endif

#ifdef ConsloePrintLog
	printf("[%04d.%02d.%02d %02d:%02d:%02d.%06ld]%s", tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,tv.tv_usec, buffer);
#endif

	return rc;
}

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

int runShellCmd(const char*cmd)
{
	int result = FAIL;
	char buf[256];
	FILE *fp;

	fp = popen(cmd, "r");
	if(fp != NULL)
	{
		while(fgets(buf, sizeof(buf), fp) != NULL)
		{
			DEBUG_INFO("%s\n", buf);
		}

		result = PASS;
	}
	pclose(fp);

	return result;
}

int StoreUsrConfigData(struct SysConfigData *UsrData)
{
	int result = PASS;
	int fd,wrd;
	unsigned int i,Chk;
	unsigned char *ptr, *BufTmp;

	Chk=0;
	ptr=(unsigned char *)UsrData;
	if((BufTmp=malloc(MtdBlockSize))!=NULL)
	{
		memset(BufTmp,0,MtdBlockSize);
		memcpy(BufTmp,ptr,sizeof(struct SysConfigData));
		for(i=ARRAY_SIZE(UsrData->CsuBootLoadFwRev);i<MtdBlockSize-4;i++)
			Chk+=*(BufTmp+i);
		memcpy(BufTmp+MtdBlockSize-4, &Chk, 4);

		// Output configuration to file.
		fd = open("/mnt/EvseConfig.bin", O_RDWR|O_CREAT);
		if (fd < 0)
		{
			DEBUG_ERROR("open /mnt/EvseConfig.bin NG\n");

			free(BufTmp);
			return 0;
		}
		wrd=write(fd, BufTmp, MtdBlockSize);
		close(fd);
		if(wrd<MtdBlockSize)
		{
			DEBUG_ERROR("write /mnt/EvseConfig.bin NG\n");

			free(BufTmp);
			return 0;
		}
		DEBUG_INFO("EvseConfig write to file in /mnt OK.\n");


		DEBUG_INFO("Erase /dev/mtd10.\n");
		runShellCmd("flash_erase /dev/mtd10 0 0");
		DEBUG_INFO("Write /dev/mtd10.\n");
		runShellCmd("nandwrite -p /dev/mtd10 /mnt/EvseConfig.bin");

		DEBUG_INFO("Erase /dev/mtd11.\n");
		runShellCmd("flash_erase /dev/mtd11 0 0");
		DEBUG_INFO("Write /dev/mtd11.\n");
		runShellCmd("nandwrite -p /dev/mtd11 /mnt/EvseConfig.bin");


		system("rm -f /mnt/EvseConfig.bin");
		DEBUG_INFO("EvseConfig write to flash OK\n");
	}
	else
	{
		DEBUG_ERROR("alloc BlockSize NG\r\n");
			result = FAIL;
	}

	if(BufTmp!=NULL)
		free(BufTmp);

	return result;
}

//==========================================
// Init all share memory
//==========================================
int InitShareMemory()
{
	int result = PASS;
	int MeterSMId;

	//Initial ShmSysConfigAndInfo
	if ((MeterSMId = shmget(ShmSysConfigAndInfoKey, sizeof(struct SysConfigAndInfo),  0777)) < 0)
    {
		DEBUG_ERROR("shmget ShmSysConfigAndInfo NG\n");
		result = FAIL;
	}
    else if ((ShmSysConfigAndInfo = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
    	DEBUG_ERROR("shmat ShmSysConfigAndInfo NG\n");
    	result = FAIL;
   	 }
    else
    {}

   	//Initial ShmStatusCodeData
   	if ((MeterSMId = shmget(ShmStatusCodeKey, sizeof(struct StatusCodeData),  0777)) < 0)
    {
   		DEBUG_ERROR("shmget ShmStatusCodeData NG\n");
   		result = FAIL;
	}
    else if ((ShmStatusCodeData = shmat(MeterSMId, NULL, 0)) == (void *) -1)
    {
    	DEBUG_ERROR("shmat ShmStatusCodeData NG\n");
    	result = FAIL;
   	}
    else
    {}


    return result;
}

//==========================================
// Main process
//==========================================
int main(void)
{
	char cmd[512] = {0};
	uint8_t retryCnt = 0;

	// Initial share memory
	if(InitShareMemory() == FAIL)
	{
		DEBUG_ERROR("InitShareMemory NG\n");

		if(ShmStatusCodeData!=NULL)
		{
			ShmStatusCodeData->AlarmCode.AlarmEvents.bits.FailToCreateShareMemory=ON;
		}
		sleep(5);
		return 0;
	}

	for(;;)
	{
		if((strlen((char*)ShmSysConfigAndInfo->SysConfig.ModelName) >= 14) &&
		   ShmSysConfigAndInfo->SysInfo.InternetConn &&
		   ShmSysConfigAndInfo->SysConfig.isReqFirstUpgrade)
		{
			system("ping 8.8.8.8 &");

			// Delete old file if exist
			runShellCmd("rm -f /mnt/LatestImage.zip");

			// Download file by model name
			DEBUG_INFO("Download %s latest image file.\n", ShmSysConfigAndInfo->SysConfig.ModelName);
			memset(cmd, 0x00, ARRAY_SIZE(cmd));
			sprintf(cmd, "wget --tries=3 -O /mnt/LatestImage.zip -c ftp://ui:ph123456@ftp.phihong.com.tw/Upgrade/%s/LatestImage.zip -T 120", ShmSysConfigAndInfo->SysConfig.ModelName);
			//DEBUG_INFO("%s\n", cmd);

			if(system(cmd) == 0)
			{
				DEBUG_INFO("File download success\n");

				DEBUG_INFO("Unzip download file\n");
				runShellCmd("unzip -oq /mnt/LatestImage.zip -d /mnt");

				DEBUG_INFO("OFF first upgrade request\n");
				ShmSysConfigAndInfo->SysConfig.isReqFirstUpgrade = OFF;
				if(StoreUsrConfigData(&ShmSysConfigAndInfo->SysConfig))
				{
					DEBUG_INFO("ShmSysConfigAndInfo->SysConfig update success\n");
				}
				else
				{
					DEBUG_INFO("ShmSysConfigAndInfo->SysConfig update fail\n");
				}

				DEBUG_INFO("Trigger upgrade request\n");
				ShmSysConfigAndInfo->SysInfo.FirmwareUpdate = ON;
			}
			else
			{
				DEBUG_ERROR("Image download fail\n");
				if(retryCnt > 10)
				{
					DEBUG_INFO("File download retry over 10 times, disable upgrade request.\n");
					ShmSysConfigAndInfo->SysConfig.isReqFirstUpgrade = OFF;
					if(StoreUsrConfigData(&ShmSysConfigAndInfo->SysConfig))
					{
						DEBUG_INFO("ShmSysConfigAndInfo->SysConfig update success\n");
					}
					else
					{
						DEBUG_INFO("ShmSysConfigAndInfo->SysConfig update fail\n");
					}
				}
				else
				{
					retryCnt += 1;
				}
			}

			// Delete download file
			runShellCmd("rm -f /mnt/LatestImage.zip");
			system("pkill ping");
		}

		sleep(10);
	}

	return FAIL;
}