/*
 * Copyright (c) 2019 TOYOTA MOTOR CORPORATION
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <fcntl.h>
#include <linux/input.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <stdbool.h>

typedef struct List
{
	int operIndex;
	int seconds;
	int microseconds;
	uint16_t event_Type;
	uint16_t event_Code;
	float event_Value;
	struct List *nextGp;
}List;

List *eventListHeader;
List *eventListLast1;
List *eventListLast2;
List *eventListLast3;

int touchScreenFd;

extern char* getTouchScreenInfo();

void deleteEventList() {
	List *prevItem = eventListHeader;
	List *nextItem = prevItem->nextGp;
	while(prevItem && nextItem) {
		free(prevItem);
		prevItem = nextItem;
		nextItem = nextItem->nextGp;
	}

	if (prevItem) {
		free(prevItem);
	}
}

void initEventList()
{
	eventListHeader = (struct List*)malloc(sizeof(struct List));

	if (!eventListHeader)
	{
		printf("init event list failed. Exit\n");
		exit(0);
	}
	else
	{
		memset(eventListHeader,0,sizeof(struct List));
		eventListHeader->nextGp = NULL;
	}

	eventListLast1 = NULL;
    eventListLast2 = NULL;
    eventListLast3 = NULL;
}

void checkTouchUp(List *temp)
{
	if((temp->event_Type == 3) && (temp->event_Code == 57) && (temp->event_Value == -1))
	{
		if(eventListLast1 == NULL)
		{
			eventListLast1 = temp;
		}
        else if(eventListLast2 == NULL)
		{
			eventListLast2 = eventListLast1;
			eventListLast1 = temp;
        }
        else
        {
            eventListLast3 = eventListLast2;
            eventListLast2 = eventListLast1;
            eventListLast1 = temp;
        }
	}
}

void deleteRedundantData()
{
    List *prevItem = eventListLast3->nextGp->nextGp->nextGp;
	List *nextItem = prevItem->nextGp;
	while(prevItem && nextItem) {
		free(prevItem);
		prevItem = nextItem;
		nextItem = nextItem->nextGp;
	}

	if (prevItem) {
		free(prevItem);
	}
}

bool tailCreatList(char *fname)
{
	List *tmpData;
	List *tail;

	char buffer[512];

	FILE *inFile;
	inFile = fopen(fname,"r");

	if (inFile == NULL)
	{
		printf("\nFailed to open the file : %s . Exit\n",fname);
		exit(0);
	}

	initEventList();
	tail = eventListHeader;

	memset(buffer,0,sizeof(buffer));

	while(fgets(buffer,sizeof(buffer),inFile))
	{
		tmpData = (struct List*)malloc(sizeof(struct List));
		if (!tmpData)
		{
			printf("Faild to create event list. Exit\n");
			fclose(inFile);
			exit(0);
		}

		memset(tmpData,0,sizeof(struct List));

		tmpData->nextGp = NULL;
		sscanf(buffer,"%d %d %d %hu %hu %f",&(tmpData->operIndex),&(tmpData->seconds),&(tmpData->microseconds),&(tmpData->event_Type),&(tmpData->event_Code),&(tmpData->event_Value));

		checkTouchUp(tmpData);
		tail->nextGp = tmpData;
		tail = tmpData;

		memset(buffer,0,sizeof(buffer));
	}

    if(eventListLast3 != NULL)
	{
        tail = eventListLast3->nextGp->nextGp;
        deleteRedundantData();
	}
    else
    {
        deleteEventList();
        return false;
    }

	tail->nextGp = NULL;

	fclose(inFile);

    return true;
}

int reportKey(int fd, uint16_t type, uint16_t code, int32_t value)
{
	struct input_event event;
	event.type = type;
	event.code = code;
	event.value = value;

	gettimeofday(&event.time, 0);

    if (write(fd, &event, sizeof(struct input_event)) < 0) {
        printf("report key error!\n");
        return -1;
    }

    return 0;
}

void touchEventTest(List *listHeader, int screenWidth, int screenHeight,
		int delayTime)
{
	List *p = listHeader->nextGp;
	printf("--------Touch Event Test Start!!!--------\n");

	while(p != NULL)
	{
		if ((p->event_Type == 3) && ((p->event_Code == 53) || (p->event_Code == 0)))
		{
			reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value * screenWidth);
		}
		else if ((p->event_Type == 3) && ((p->event_Code == 54) || (p->event_Code == 1)))
		{
			reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value * screenHeight);
		}
		else
		{
			reportKey(touchScreenFd,p->event_Type,p->event_Code,p->event_Value);
		}

		if (p->nextGp != NULL)
		{
			usleep((p->nextGp->seconds - p->seconds) * 1000000 + (p->nextGp->microseconds - p->microseconds));
		}

		p = p->nextGp;

	}
	printf("--------Touch Event Test End!!!--------\n");
}

int appNeedEventTestStart(int tsWidth, int tsHeight, char* eventNode,
		char* eventFile, int delayTime)
{
	touchScreenFd = open(eventNode, O_RDWR);

	if (touchScreenFd < 0)
	{
		printf("Open %s failed.\n", eventNode);
		return -1;
	}

    if(tailCreatList(eventFile))
    {
        touchEventTest(eventListHeader, tsWidth, tsHeight, delayTime);
        deleteEventList();
    }

    close(touchScreenFd);

	return 1;
}

void printUsage(char* myName)
{
	printf("usage:\n \
	%s -a <string> -d <number> -c <string> -e <number>  -f <string>\n \
	-a : application name(must)\n \
	-d : the time interval(> 0) for capturing logs(unit:ms)(must).\n \
	-c : child process name(s).If more than one, sperated by ','. \n\t\tEg. -c process1,prcocess2,process3.\n \
	-f : file name for recorded event. If -e is 1, then -f must be specified.\n \
	-e : 1 for apps that need events, 0 for apps that does not need events.Default is 0. \n", myName);
}

int main(int argc,char *argv[])
{
	int index = 0;
	int screenWidth, screenHeight;
	char eventFileName[100];
	char eventNode[50];
	const char* tsInfo;
	int needEvents = 0;
	int delayTime = 0;

	if (argc < 5)
	{
		printUsage(argv[0]);
		return -1;
	}

	memset(eventFileName, 0, sizeof(eventFileName));
	memset(eventNode, 0, sizeof(eventNode));

	for (index = 1; index < argc; index ++)
	{
		if (strcmp(argv[index], "-f") == 0)
		{
			memcpy(eventFileName, argv[index + 1], strlen(argv[index + 1]) + 1);
			index++;
		}
		else if (strcmp(argv[index], "-d") == 0)
		{
			delayTime = atoi(argv[++index]);
		}
		else if (strcmp(argv[index], "-e") == 0)
		{
			needEvents = atoi(argv[++index]);
		}
		else
		{
			printUsage(argv[0]);
			return -1;
		}
	}

	if (0 == delayTime) {
		printf("Please specify an interval time(> 0)\n");
		printUsage(argv[0]);
		return -1;
	}

	printf("step %d. \n", __LINE__);
	if ((1 == needEvents) && (0 == strlen(eventFileName))) {
		printf("You should specify a file for recorded event.\n");
		printUsage(argv[0]);
		return -1;
	}
	printf("step %d. \n", __LINE__);

	if (1 == needEvents)
	{
		printf("step %d. \n", __LINE__);
		tsInfo = getTouchScreenInfo();
        printf("step %d. \n", __LINE__);
		if (NULL == tsInfo) {
			printf("Failed to get touch screen info.\n");
			return -1;
		}
		printf("step %d. tsInfo = %s. \n", __LINE__, tsInfo);

		sscanf(tsInfo, "%s %d %d", eventNode, &screenWidth, &screenHeight);
		appNeedEventTestStart(screenWidth, screenHeight, eventNode, eventFileName,
						delayTime);
		printf("step %d. \n", __LINE__);

	}
	return 0;
}