/************************************************************
* Copyright (C) 2006-2007 Masahiko SAWAI All Rights Reserved. 
************************************************************/

#include "wiiremote_impl.h"
#include "DebugLog.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

#include <unistd.h>
#include <sys/socket.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/l2cap.h>


#define INQUIRY_MAX_RSP 255

#define OUTPUT_BUFFER_SIZE (WRMT_BUFFER_SIZE+1)
#define INPUT_BUFFER_SIZE 32

/************************************************************
#type definitions
************************************************************/
struct wrmt_wiiremote_impl
{
	bdaddr_t bdaddr;
	int control_socket;
	int interrupt_socket;
	unsigned char input_buffer[INPUT_BUFFER_SIZE];
	unsigned char output_buffer[OUTPUT_BUFFER_SIZE];
} ;

#define WRMT_Impl_Invaliant(self) \
{ \
	assert((self) != NULL); \
	assert((self)->control_socket == -1 || (self)->control_socket > 0); \
	assert((self)->interrupt_socket == -1 || (self)->interrupt_socket > 0); \
}

/************************************************************
#private variables
************************************************************/

static
WRMT_WiiRemoteImpl 
wiiRemoteImplList[WRMT_MAX_DEVICES];

static
int 
wiiRemoteImplList_index = 0;

/************************************************************
#private functions
************************************************************/

static
int
IsWiiRemoteOnLinux(int dev_descriptor, inquiry_info *info)
{
	int result = 0;
	assert(info != NULL);
	DebugLog("Hello\n");

	if ( info->dev_class[0] == 0x04 &&
		info->dev_class[1] == 0x25 &&
		info->dev_class[2] == 0x00)
	{
#ifdef WRMT_DEVICE_NAME_CHECK
		int rc;
		char name[248];

		rc = hci_read_remote_name(dev_descriptor,
			&(info->bdaddr),
			sizeof(name)/sizeof(char), name, 0);
		if (rc == 0)
		{
			if (strcmp(WRMT_DEVICE_NAME, name) == 0)
			{
				result = 1;
			}
		}
#else
		result = 1;
#endif
	}

	DebugLog("Bye\n");
	assert(result == 1 || result == 0);
	return result;
}

static
void
WRMT_WiiRemoteImpl_InitWithAddress(WRMT_WiiRemoteImpl *self,
	bdaddr_t bdaddr)
{
	DebugLog("Hello\n");

	self->bdaddr = bdaddr;
	self->control_socket = -1;
	self->interrupt_socket = -1;
	memset(self->output_buffer, 0, sizeof(self->output_buffer)/sizeof(unsigned char));
	memset(self->input_buffer, 0, sizeof(self->input_buffer)/sizeof(unsigned char));

	DebugLog("Bye\n");
	WRMT_Impl_Invaliant(self);
}


#if 0
static
WRMT_IOReturn
WRMT_Impl_ReadFromDevice(WRMT_WiiRemoteImpl *self, int timeout_in_msec)
{
	WRMT_IOReturn result = WRMT_IO_ERROR;
	fd_set input_set;
	struct timeval timeout;
	WRMT_Impl_Invaliant(self);

	timeout.tv_sec = timeout_in_msec / 1000;
	timeout.tv_usec = (timeout_in_msec % 1000) * 1000;
	memset(self->input_buffer, 0, sizeof(unsigned char) * INPUT_BUFFER_SIZE);

	FD_ZERO(&input_set);
	FD_SET(self->interrupt_socket, &input_set);

	select(self->interrupt_socket+1, &input_set, NULL, NULL, &timeout);
	if (FD_ISSET(self->interrupt_socket, &input_set))
	{
		int recieved_size = recv(self->interrupt_socket, self->input_buffer, INPUT_BUFFER_SIZE, 0);
		if (recieved_size > 0)
		{
			result = WRMT_IO_SUCCESS;
		}
	}
	else
	{
		result = WRMT_IO_TIMEOUT;
	}

	WRMT_Impl_Invaliant(self);
	return result;
}
#endif

/************************************************************
#public functions
************************************************************/
int
WRMT_Impl_Init(int inquiry_length_in_sec)
{
	int result = 0;
	int dev_id, dev_descriptor;
	int inquiry_length;
	int num_rsp;
	inquiry_info inquiry_info_list[INQUIRY_MAX_RSP];
	inquiry_info *ii = inquiry_info_list;
	DebugLog("Hello\n");

	dev_id = hci_get_route(NULL);
	dev_descriptor = hci_open_dev(dev_id);

	inquiry_length = (int)inquiry_length_in_sec * 1.28;
	if (inquiry_length <= 0) inquiry_length = 1;

	num_rsp = hci_inquiry(dev_id, inquiry_length,
		INQUIRY_MAX_RSP, NULL, &ii, IREQ_CACHE_FLUSH);

	if (num_rsp >= 0)
	{
		int i;
		for (i = 0;i < num_rsp;i++)
		{
			if (IsWiiRemoteOnLinux(dev_descriptor, &(inquiry_info_list[i])))
			{
				WRMT_WiiRemoteImpl_InitWithAddress(
					&(wiiRemoteImplList[wiiRemoteImplList_index]), 
					inquiry_info_list[i].bdaddr);

				wiiRemoteImplList_index++;
				if (wiiRemoteImplList_index >= WRMT_MAX_DEVICES)
				{
					break;
				}
			}
		}
	}
	else
	{
		result = -1;
	}

	hci_close_dev(dev_descriptor);

	DebugLog("Bye\n");
	assert(result == 0 || result == -1);
	return result;
}

void
WRMT_Impl_Quit()
{
	int i;
	DebugLog("Hello\n");

	for (i = 0;i < wiiRemoteImplList_index;i++)
	{
		WRMT_WiiRemoteImpl_Close(&(wiiRemoteImplList[i]));
	}
	wiiRemoteImplList_index = 0;

	DebugLog("Bye\n");
}

void
WRMT_Impl_Sleep(int ms)
{
	usleep(ms * 1000);
}

int
WRMT_Impl_GetNumWiiRemote()
{
	DebugLog("Hello\n");
	return wiiRemoteImplList_index;
}

WRMT_WiiRemoteImpl *
WRMT_Impl_GetWiiRemoteAt(int index)
{
	WRMT_WiiRemoteImpl *result;
	assert(index >= 0 && index < wiiRemoteImplList_index);
	DebugLog("Hello index : %d\n", index);

	result = &(wiiRemoteImplList[index]);

	DebugLog("Bye result : %p\n", result);
	return result;
}

WRMT_IOReturn
WRMT_Impl_Poll(int *updated_device_index_pointer)
{
	WRMT_IOReturn result = WRMT_IO_ERROR;
	struct timeval timeout;
	fd_set input_set;
	int i, max_fd, rc;
	DebugLog("Hello\n");

	/*
	timeout.tv_sec = timeout_in_msec / 1000;
	timeout.tv_usec = (timeout_in_msec % 1000) * 1000;
	*/
	timeout.tv_sec = 0;
	timeout.tv_usec = 0;

	FD_ZERO(&input_set);
	max_fd = -1;
	for (i = 0;i < wiiRemoteImplList_index;i++)
	{
		WRMT_WiiRemoteImpl *wiiRemoteImpl = &(wiiRemoteImplList[i]);
		if (WRMT_WiiRemoteImpl_IsOpened(wiiRemoteImpl))
		{
			FD_SET(wiiRemoteImpl->interrupt_socket, &input_set);
			if (wiiRemoteImpl->interrupt_socket > max_fd) 
			{
				max_fd = wiiRemoteImpl->interrupt_socket;
			}
		}
	}

	rc = select(max_fd+1, &input_set, NULL, NULL, &timeout);
	if(rc > 0)
	{
		for (i = 0;i < wiiRemoteImplList_index;i++)
		{
			WRMT_WiiRemoteImpl *wiiRemoteImpl = &(wiiRemoteImplList[i]);
			if (FD_ISSET(wiiRemoteImpl->interrupt_socket, &input_set))
			{
				int recieved_size = recv(wiiRemoteImpl->interrupt_socket,
					wiiRemoteImpl->input_buffer,
					INPUT_BUFFER_SIZE, 0);
				if (recieved_size > 0)
				{
					result = WRMT_IO_SUCCESS;
					if (updated_device_index_pointer != NULL)
					{
						*updated_device_index_pointer = i;
					}
				}
				else
				{
					// FIXME : set error message 
					result = WRMT_IO_ERROR;
				}
				break;
			}
		}
	}
	else if (rc == 0)
	{
		result = WRMT_IO_TIMEOUT;
	}
	else if (rc < 0)
	{
		result = WRMT_IO_ERROR;
		// FIXME : set error message
	}

	DebugLog("Bye\n");
	return result;
}


WRMT_IOReturn
WRMT_WiiRemoteImpl_Open(WRMT_WiiRemoteImpl *self)
{
	WRMT_IOReturn result = WRMT_IO_SUCCESS;
	WRMT_Impl_Invaliant(self);
	DebugLog("Hello\n");

	if (!WRMT_WiiRemoteImpl_IsOpened(self))
	{
		int control_socket = -1, interrupt_socket = -1;
		struct sockaddr_l2 l2addr = { 0 };
		int rc;

		/* Create Control Channel Socket */
		control_socket = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
		if(control_socket == -1)
		{
			WRMT_SetError("Control Socket Creation Failed.");
			result = WRMT_IO_ERROR;
			return result;
		}

		l2addr.l2_family = AF_BLUETOOTH;
		l2addr.l2_bdaddr = self->bdaddr;
		l2addr.l2_psm = htobs(WRMT_CONTROL_CHANNEL_PSM);
		rc = connect(control_socket, (struct sockaddr *)&l2addr, sizeof(l2addr));
		if(rc == -1)
		{
			WRMT_SetError("Control Socket Connecting Failed.");
			result = WRMT_IO_ERROR;
			return result;
		}

		/* Create Interrupt Channel Socket */
		interrupt_socket = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
		if(interrupt_socket == -1)
		{
			WRMT_SetError("Interrupt Socket Creation Failed.");
			result = WRMT_IO_ERROR;
			close(control_socket);
			return result;
		}

		l2addr.l2_family = AF_BLUETOOTH;
		l2addr.l2_bdaddr = self->bdaddr;
		l2addr.l2_psm = htobs(WRMT_INTERRUPT_CHANNEL_PSM);
		rc = connect(interrupt_socket, (struct sockaddr *)&l2addr,
			sizeof(l2addr));
		if(rc == -1)
		{
			WRMT_SetError("Interrupt Socket Connecting Failed.");
			result = WRMT_IO_ERROR;
			close(control_socket);
			return result;
		}

		/* init */
		self->control_socket = control_socket;
		self->interrupt_socket = interrupt_socket;
		result = WRMT_IO_SUCCESS;
	}

	DebugLog("Bye\n");
	WRMT_Impl_Invaliant(self);
	return result;
}


int
WRMT_WiiRemoteImpl_IsOpened(WRMT_WiiRemoteImpl *self)
{
	int result = 0;
	WRMT_Impl_Invaliant(self);
	DebugLog("Hello\n");

	if (self->control_socket != -1 &&
		self->interrupt_socket != -1)
	{
		result = 1;
	}

	DebugLog("Bye\n");
	WRMT_Impl_Invaliant(self);
	return result;
}

void
WRMT_WiiRemoteImpl_Close(WRMT_WiiRemoteImpl *self)
{
	WRMT_Impl_Invaliant(self);
	DebugLog("Hello\n");

	if (self->control_socket != -1)
	{
		close(self->control_socket);
		self->control_socket = -1;
	}
	if (self->interrupt_socket != -1)
	{
		close(self->interrupt_socket);
		self->interrupt_socket = -1;
	}

	DebugLog("Bye\n");
	WRMT_Impl_Invaliant(self);
}

unsigned char *
WRMT_WiiRemoteImpl_GetOutputBuffer(WRMT_WiiRemoteImpl *self)
{
	DebugLog("Hello\n");
	return (self->output_buffer+1);
}

unsigned char *
WRMT_WiiRemoteImpl_GetInputBuffer(WRMT_WiiRemoteImpl *self)
{
	DebugLog("Hello\n");
	return (self->input_buffer+1);
}

WRMT_IOReturn
WRMT_WiiRemoteImpl_OutputToDevice(WRMT_WiiRemoteImpl *self)
{
	unsigned char header, report_id;
	size_t report_size, sended_size, recieved_size;
	WRMT_Impl_Invaliant(self);
	assert( (self->output_buffer[1] >= WRMT_OUTPUT_REPORT_ID_FIRST) &&
		(self->output_buffer[1] <= WRMT_OUTPUT_REPORT_ID_LAST) );
	DebugLog("Hello\n");

	/* send request */
	self->output_buffer[0] = 0x52;
	report_id = self->output_buffer[1];
	report_size = WRMT_Impl_GetOutputReportSize(report_id);
	sended_size = send(self->control_socket,
		self->output_buffer, report_size+1, 0);
	if (sended_size < 0)
	{
		WRMT_SetError("Send Request Failed.");
		return WRMT_IO_ERROR;
	}

	/* read status */
	recieved_size = recv(self->control_socket, &header, 1, 0);
	if (recieved_size < 0)
	{
		WRMT_SetError("Recieve Request Failed.");
		return WRMT_IO_ERROR;
	}

	/* check status */
	if (header != 0)
	{
		WRMT_SetError("Recieve Illegal Header.");
		return WRMT_IO_ERROR;
	}

	DebugLog("Bye\n");
	WRMT_Impl_Invaliant(self);
	return WRMT_IO_SUCCESS;
}

