/************************************************************
* Copyright (C) 2008 Masahiko SAWAI All Rights Reserved. 
************************************************************/
#include <stdio.h>
#include <ruby.h>

#include <wiiremote.h>
#include <wiiremote_utils.h>

/**
* WRMT module 
**/

static
VALUE
WRMT_init(VALUE klass)
{
	int rc, number, i;
	VALUE WiiRemote;
	VALUE wiiremotes;

	/* init libwiiremote */
	rc = WRMT_Init();
	if (rc != 0)
	{
		rb_raise(rb_eStandardError, "libwiiremote initialization failed(code %d)", rc);
	}

	/* init array (wiiremotes) */
	WiiRemote = rb_const_get(klass, rb_intern("WiiRemote"));
	wiiremotes = rb_ivar_get(klass, rb_intern("wiiremotes"));
	rb_ary_clear(wiiremotes);
	number = WRMT_GetNumWiiRemote();
	for (i = 0;i < number;i++)
	{
		VALUE wiiremote;

		wiiremote = rb_funcall(WiiRemote, rb_intern("new"), 1, INT2FIX(i));
		rb_ary_push(wiiremotes, wiiremote);
	}

	return Qnil;
}

static
VALUE
WRMT_quit(VALUE klass)
{
	WRMT_Quit();
	return Qnil;
}

static
VALUE
WRMT_update(VALUE klass)
{
	WRMT_Update();
	return Qnil;
}

static
VALUE
WRMT_poll(VALUE klass)
{
	return INT2NUM(WRMT_Poll());
}

static
VALUE
WRMT_num_wiiremote(VALUE klass)
{
	return INT2NUM(WRMT_GetNumWiiRemote());
}

static
VALUE
WRMT_wiiremote_at(VALUE klass, VALUE device_index_value)
{
	VALUE result;
	VALUE wiiremotes;

	wiiremotes = rb_ivar_get(klass, rb_intern("wiiremotes"));
	result = rb_ary_entry(wiiremotes, NUM2LONG(device_index_value));
	
	return result;
}

static
VALUE
WRMT_wiiremotes(VALUE klass)
{
	return rb_ivar_get(klass, rb_intern("wiiremotes"));
}


/**
* WRMT::WiiRemote class
**/

static
VALUE
WiiRemote_alloc(VALUE klass)
{
	VALUE rdata = Data_Wrap_Struct(klass, 0, 0, NULL);
	return rdata;
}

static
VALUE
WiiRemote_initialize(VALUE self, VALUE device_index_value)
{
	WRMT_WiiRemote *wiiremote = NULL;
	int device_index, number_of_devices;

	number_of_devices = WRMT_GetNumWiiRemote();
	device_index = NUM2INT(device_index_value);
	if (device_index < 0 || device_index >= number_of_devices)
	{
		rb_raise(rb_eIndexError, "device index %d is out of range (0 - %d)", device_index, number_of_devices-1);
	}

	wiiremote = WRMT_GetWiiRemoteAt(device_index);
	RDATA(self)->data = wiiremote;

	return Qnil;
}

static
VALUE
WiiRemote_open(VALUE self)
{
	VALUE result = Qfalse;
	WRMT_WiiRemote *wiiremote;
	WRMT_IOReturn rc;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	rc = WRMT_WiiRemote_Open(wiiremote);
	if(rc == WRMT_IO_SUCCESS)
	{
		result = Qtrue;
	}

	return result;
}

static
VALUE
WiiRemote_opened_p(VALUE self)
{
	VALUE result = Qfalse;
	WRMT_WiiRemote *wiiremote;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);
	if(WRMT_WiiRemote_IsOpened(wiiremote))
	{
		result = Qtrue;
	}

	return result;
}

static
VALUE
WiiRemote_close(VALUE self)
{
	WRMT_WiiRemote *wiiremote;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);
	WRMT_WiiRemote_Close(wiiremote);

	return Qnil;
}

static
VALUE
WiiRemote_enabled_p(VALUE self, VALUE function_type_value)
{
	VALUE result = Qfalse;
	WRMT_WiiRemote *wiiremote;
	int function_type;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	function_type = NUM2INT(function_type_value);
	if (function_type < 0 && function_type >= WRMT_NUMBER_OF_FUNCTIONS)
	{
		rb_raise(rb_eIndexError, "function type %d is out of range (0 - %d)", function_type, WRMT_NUMBER_OF_FUNCTIONS-1);
	}

	if (WRMT_WiiRemote_IsEnabled(wiiremote, function_type))
	{
		result = Qtrue;
	}

	return result;
}

static
VALUE
WiiRemote_set_enabled(VALUE self, VALUE function_type_value, VALUE value_value)
{
	WRMT_WiiRemote *wiiremote;
	int function_type;
	int value;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	function_type = NUM2INT(function_type_value);
	if (function_type < 0 && function_type >= WRMT_NUMBER_OF_FUNCTIONS)
	{
		rb_raise(rb_eIndexError,
			"function type %d is out of range (0 - %d)",
			function_type, WRMT_NUMBER_OF_FUNCTIONS-1);
	}

	if (value_value == Qtrue)
	{
		value = 1;
	}
	else if (value_value == Qfalse)
	{
		value = 0;
	}
	else
	{
		value = NUM2INT(value_value);
		if (value != 0) value = 1;
	}

	WRMT_WiiRemote_SetEnabled(wiiremote, function_type, value);

	return Qnil;
}

static
VALUE
WiiRemote_state(VALUE self, VALUE data_type_value)
{
	VALUE result = Qnil;
	WRMT_WiiRemote *wiiremote;
	int data_type, data;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	data_type = NUM2INT(data_type_value);
	if (data_type < 0 && data_type >= WRMT_NUMBER_OF_DATA_TYPE)
	{
		rb_raise(rb_eIndexError, "data type %d is out of range (0 - %d)",
			data_type, WRMT_NUMBER_OF_DATA_TYPE-1);
	}
	
	data = WRMT_WiiRemote_GetState(wiiremote, data_type);
	result = INT2NUM(data);

	return result;
}

static
VALUE
WiiRemote_set_state(VALUE self, VALUE data_type_value, VALUE data_value)
{
	WRMT_WiiRemote *wiiremote;
	int data_type, data;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	data_type = NUM2INT(data_type_value);
	if (data_type < 0 && data_type >= WRMT_NUMBER_OF_DATA_TYPE)
	{
		rb_raise(rb_eIndexError, "data type %d is out of range (0 - %d)",
			data_type, WRMT_NUMBER_OF_DATA_TYPE-1);
	}

	data = NUM2INT(data_value);

	WRMT_WiiRemote_SetState(wiiremote, data_type, data);

	return Qnil;
}

static
int
button_mask_from_value(VALUE button_mask_value)
{
	int button_mask = 0;

	switch (TYPE(button_mask_value))
	{
	case T_STRING :
		{
			int len = RSTRING(button_mask_value)->len;
			void *ptr = RSTRING(button_mask_value)->ptr;
			
			if (memchr(ptr, '2', len) != NULL) button_mask |= WRMT_MASK_BUTTON_TWO;
			if (memchr(ptr, '1', len) != NULL) button_mask |= WRMT_MASK_BUTTON_ONE;
			if (memchr(ptr, 'B', len) != NULL) button_mask |= WRMT_MASK_BUTTON_B;
			if (memchr(ptr, 'A', len) != NULL) button_mask |= WRMT_MASK_BUTTON_A;
			if (memchr(ptr, '-', len) != NULL) button_mask |= WRMT_MASK_BUTTON_MINUS;
			if (memchr(ptr, 'H', len) != NULL) button_mask |= WRMT_MASK_BUTTON_HOME;
			if (memchr(ptr, 'L', len) != NULL) button_mask |= WRMT_MASK_BUTTON_LEFT;
			if (memchr(ptr, 'R', len) != NULL) button_mask |= WRMT_MASK_BUTTON_RIGHT;
			if (memchr(ptr, 'D', len) != NULL) button_mask |= WRMT_MASK_BUTTON_DOWN;
			if (memchr(ptr, 'U', len) != NULL) button_mask |= WRMT_MASK_BUTTON_UP;
			if (memchr(ptr, '+', len) != NULL) button_mask |= WRMT_MASK_BUTTON_PLUS;
		}
		break;
	default:
		button_mask = NUM2INT(button_mask_value);
		break;
	}

	return button_mask;
}

static
VALUE
WiiRemote_check_button_any(VALUE self, VALUE button_mask_value)
{
	VALUE result = Qfalse;
	WRMT_WiiRemote *wiiremote;
	int button_mask;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	button_mask = button_mask_from_value(button_mask_value);
	if (WRMT_WiiRemote_CheckButtonAny(wiiremote, button_mask))
	{
		result = Qtrue;
	}

	return result;
}

static
VALUE
WiiRemote_check_button_all(VALUE self, VALUE button_mask_value)
{
	VALUE result = Qfalse;
	WRMT_WiiRemote *wiiremote;
	int button_mask;

	Data_Get_Struct(self, WRMT_WiiRemote, wiiremote);

	button_mask = button_mask_from_value(button_mask_value);
	if (WRMT_WiiRemote_CheckButtonAll(wiiremote, button_mask))
	{
		result = Qtrue;
	}

	return result;
}

void Init_wiiremote()
{
	VALUE WRMT, WiiRemote;

	/**
	* WRMT
	**/
	WRMT = rb_define_module("WRMT");
	rb_ivar_set(WRMT, rb_intern("wiiremotes"), rb_ary_new());

	/* button mask */
	rb_define_const(WRMT, "MASK_BUTTON_TWO",   INT2FIX(WRMT_MASK_BUTTON_TWO));
	rb_define_const(WRMT, "MASK_BUTTON_ONE",   INT2FIX(WRMT_MASK_BUTTON_ONE));
	rb_define_const(WRMT, "MASK_BUTTON_B",     INT2FIX(WRMT_MASK_BUTTON_B));
	rb_define_const(WRMT, "MASK_BUTTON_A",     INT2FIX(WRMT_MASK_BUTTON_A));
	rb_define_const(WRMT, "MASK_BUTTON_MINUS", INT2FIX(WRMT_MASK_BUTTON_MINUS));
	rb_define_const(WRMT, "MASK_BUTTON_HOME",  INT2FIX(WRMT_MASK_BUTTON_HOME));
	rb_define_const(WRMT, "MASK_BUTTON_LEFT",  INT2FIX(WRMT_MASK_BUTTON_LEFT));
	rb_define_const(WRMT, "MASK_BUTTON_RIGHT", INT2FIX(WRMT_MASK_BUTTON_RIGHT));
	rb_define_const(WRMT, "MASK_BUTTON_DOWN",  INT2FIX(WRMT_MASK_BUTTON_DOWN));
	rb_define_const(WRMT, "MASK_BUTTON_UP",    INT2FIX(WRMT_MASK_BUTTON_UP));
	rb_define_const(WRMT, "MASK_BUTTON_PLUS",  INT2FIX(WRMT_MASK_BUTTON_PLUS));
	rb_define_const(WRMT, "MASK_BUTTONS",      INT2FIX(WRMT_MASK_BUTTONS));

	/* LED mask */
	rb_define_const(WRMT, "MASK_LED_1", INT2FIX(WRMT_MASK_LED_1));
	rb_define_const(WRMT, "MASK_LED_2", INT2FIX(WRMT_MASK_LED_2));
	rb_define_const(WRMT, "MASK_LED_3", INT2FIX(WRMT_MASK_LED_3));
	rb_define_const(WRMT, "MASK_LED_4", INT2FIX(WRMT_MASK_LED_4));
	rb_define_const(WRMT, "MASK_LEDS",  INT2FIX(WRMT_MASK_LEDS));

	/* function type */
	rb_define_const(WRMT, "FUNCTION_CONTINUOUS", INT2FIX(WRMT_FUNCTION_CONTINUOUS));
	rb_define_const(WRMT, "FUNCTION_MOTION",     INT2FIX(WRMT_FUNCTION_MOTION));
	rb_define_const(WRMT, "FUNCTION_IR",         INT2FIX(WRMT_FUNCTION_IR));
	rb_define_const(WRMT, "FUNCTION_SPEAKER",    INT2FIX(WRMT_FUNCTION_SPEAKER));

	/* read write data type */
	rb_define_const(WRMT, "DATA_FORCE_FEEDBACK", INT2FIX(WRMT_DATA_FORCE_FEEDBACK));
	rb_define_const(WRMT, "DATA_LEDS",           INT2FIX(WRMT_DATA_LEDS));
	rb_define_const(WRMT, "DATA_SPEAKER_FORMAT", INT2FIX(WRMT_DATA_SPEAKER_FORMAT));
	rb_define_const(WRMT, "DATA_SPEAKER_VOLUME", INT2FIX(WRMT_DATA_SPEAKER_VOLUME));
	rb_define_const(WRMT, "DATA_SPEAKER_SAMPLE_RATE", INT2FIX(WRMT_DATA_SPEAKER_SAMPLE_RATE));
	/* read only data type */
	rb_define_const(WRMT, "DATA_BUTTONS",        INT2FIX(WRMT_DATA_BUTTONS));
	rb_define_const(WRMT, "DATA_MOTION_X",       INT2FIX(WRMT_DATA_MOTION_X));
	rb_define_const(WRMT, "DATA_MOTION_Y",       INT2FIX(WRMT_DATA_MOTION_Y));
	rb_define_const(WRMT, "DATA_MOTION_Z",       INT2FIX(WRMT_DATA_MOTION_Z));
	rb_define_const(WRMT, "DATA_IR1_FOUND",      INT2FIX(WRMT_DATA_IR1_FOUND));
	rb_define_const(WRMT, "DATA_IR1_SIZE",       INT2FIX(WRMT_DATA_IR1_SIZE));
	rb_define_const(WRMT, "DATA_IR1_X",          INT2FIX(WRMT_DATA_IR1_X));
	rb_define_const(WRMT, "DATA_IR1_Y",          INT2FIX(WRMT_DATA_IR1_Y));
	rb_define_const(WRMT, "DATA_IR2_FOUND",      INT2FIX(WRMT_DATA_IR2_FOUND));
	rb_define_const(WRMT, "DATA_IR2_SIZE",       INT2FIX(WRMT_DATA_IR2_SIZE));
	rb_define_const(WRMT, "DATA_IR2_X",          INT2FIX(WRMT_DATA_IR2_X));
	rb_define_const(WRMT, "DATA_IR2_Y",          INT2FIX(WRMT_DATA_IR2_Y));
	rb_define_const(WRMT, "DATA_IR_FOUND",       INT2FIX(WRMT_DATA_IR_FOUND));
	rb_define_const(WRMT, "DATA_IR_SIZE",        INT2FIX(WRMT_DATA_IR_SIZE));
	rb_define_const(WRMT, "DATA_IR_X",           INT2FIX(WRMT_DATA_IR_X));
	rb_define_const(WRMT, "DATA_IR_Y",           INT2FIX(WRMT_DATA_IR_Y));

	rb_define_const(WRMT, "IO_TIMEOUT",          INT2FIX(WRMT_IO_TIMEOUT));
	rb_define_const(WRMT, "IO_ERROR",            INT2FIX(WRMT_IO_ERROR));
	rb_define_const(WRMT, "IO_SUCCESS",          INT2FIX(WRMT_IO_SUCCESS));

	/* class method */
	rb_define_singleton_method(WRMT, "init", WRMT_init, 0);
	rb_define_singleton_method(WRMT, "quit", WRMT_quit, 0);
	rb_define_singleton_method(WRMT, "update", WRMT_update, 0);
	rb_define_singleton_method(WRMT, "poll", WRMT_poll, 0);
	rb_define_singleton_method(WRMT, "num_wiiremote", WRMT_num_wiiremote, 0);
	rb_define_singleton_method(WRMT, "wiiremote_at", WRMT_wiiremote_at, 1);
	rb_define_singleton_method(WRMT, "wiiremotes", WRMT_wiiremotes, 0);

	/**
	* WRMT::WiiRemote
	**/
	WiiRemote = rb_define_class_under(WRMT, "WiiRemote", rb_cObject);
	rb_define_alloc_func(WiiRemote, WiiRemote_alloc);
	rb_define_private_method(WiiRemote, "initialize", WiiRemote_initialize, 1);
	rb_define_method(WiiRemote, "open", WiiRemote_open, 0);
	rb_define_method(WiiRemote, "opened?", WiiRemote_opened_p, 0);
	rb_define_method(WiiRemote, "close", WiiRemote_close, 0);
	rb_define_method(WiiRemote, "enabled?", WiiRemote_enabled_p, 1);
	rb_define_method(WiiRemote, "set_enabled", WiiRemote_set_enabled, 2);
	rb_define_method(WiiRemote, "state", WiiRemote_state, 1);
	rb_define_alias(WiiRemote, "[]", "state");
	rb_define_method(WiiRemote, "set_state", WiiRemote_set_state, 2);
	rb_define_alias(WiiRemote, "[]=", "set_state");

	/* utility methods */
	rb_define_method(WiiRemote, "check_button_any", WiiRemote_check_button_any, 1);
	rb_define_method(WiiRemote, "check_button_all", WiiRemote_check_button_all, 1);
	rb_define_alias(WiiRemote, "check_button", "check_button_any");
}
