/*
 * Copyright (c) 2017 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.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <net/if.h>
#include <string.h>
#include <sys/ioctl.h>
#include <alloca.h>

#include <linux/input.h>
#include <linux/joystick.h>

#include "af-steering-wheel-binding.h"
#include "js_raw.h"
#include "js_signal_event.h"

#define JSNAMELEN 128

static int *axis;
static char *button;

int js_signal_read(int fd)
{
	ssize_t size;
	struct js_event jsEvent;
	int rc = 0;

	size = read(fd, &jsEvent, sizeof(struct js_event));
	if(size != 0)
	{
		switch (jsEvent.type & ~JS_EVENT_INIT)
		{
			case JS_EVENT_BUTTON:
				button[jsEvent.number] = (char)jsEvent.value;
				//NOTICEMSG("JS_EVENT_BUTTON %d\n", button[jsEvent.number]);
				newButtonValue(jsEvent.number, jsEvent.value);
				break;
			case JS_EVENT_AXIS:
				axis[jsEvent.number] = jsEvent.value;
				//NOTICEMSG("JS_EVENT_AXIS %d\n", axis[jsEvent.number]);
				newAxisValue(jsEvent.number, jsEvent.value);
				break;
			default:
				break;
		}

		rc = 0;
	}
	else
	{
		rc = -1;
	}

	return rc;
}

int on_event(sd_event_source *s, int fd, uint32_t revents, void *userdata)
{
	if ((revents & EPOLLIN) != 0)
	{
		//NOTICEMSG("on_event!\n");
		int rc = 0;

		rc = js_signal_read(fd);
		if(rc == -1)
		{
			ERRMSG("JS Frame Read failed");

			return -1;
		}
	}
	if ((revents & (EPOLLERR|EPOLLRDHUP|EPOLLHUP)) != 0)
	{
		/* T.B.D
		 * if error or hungup */
		ERRMSG("Error or Hunup: rvent=%08x", revents);
	}

	return 0;
}

int  js_open(const char *devname)
{
	unsigned char numAxes = 0;
	unsigned char numButtons = 0;
	int version = 0;
	int fd;
	char name[JSNAMELEN] = "Unknown";

	struct js_corr cal[6];
	int i, j;
	unsigned int calData[36] =
	{
		1, 0, 8191, 8192, 65542, 65534,
		1, 0, 127, 128, 4227201, 4194176,
		1, 0, 127, 128, 4227201, 4194176,
		1, 0, 127, 128, 4227201, 4194176,
		1, 0, 0, 0, 536854528, 536854528,
		1, 0, 0, 0, 536854528, 536854528
	};

	if ((fd = open(devname, O_RDONLY)) < 0)
	{
		return -1;
	}

	ioctl(fd, JSIOCGVERSION, &version);
	ioctl(fd, JSIOCGAXES, &numAxes);
	ioctl(fd, JSIOCGBUTTONS, &numButtons);
	ioctl(fd, JSIOCGNAME(JSNAMELEN), name);

	for (i = 0; i < 6; i++)
	{
		int k = 0;
		cal[i].type = (__u16)calData[(i*6)+k];
		k++;
		cal[i].prec = (__s16)calData[(i*6)+k];
		k++;

		for(j = 0; j < 4; j++)
		{
			cal[i].coef[j] = (__s32)calData[(i*6)+k];
			k++;
		}
	}

	if (ioctl(fd, JSIOCSCORR, &cal) < 0)
	{
		return -1;
	}

	axis = (int *)calloc(numAxes, sizeof(int));
	button = (char *)calloc(numButtons, sizeof(char));
#if 0
	gis = g_unix_input_stream_new(fd, TRUE);
	if(gis == NULL)
	{
		ERRMSG("g_unix_input_stream_new() failed!");
	}
	else
	{
		NOTICEMSG("g_unix_input_stream_new() succeed!");
	}
	g_input_stream_read_async(gis, &jsEvent, sizeof(struct js_event), G_PRIORITY_DEFAULT, NULL, &readCallback, NULL);
#endif

	return fd;
}

void js_close(int js)
{
	if (js < 0)
	{
		return;
	}

	close(js);
}