/*
 * file x11_joystick.c - joystick support for linux
 *
 * $Id: x11_joystick.c,v 1.5 2006/02/09 21:21:25 fzago Exp $
 *
 * Program XBLAST 
 * (C) by Oliver Vogel (e-mail: m.vogel@ndh.net)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published
 * by the Free Software Foundation; either version 2; or (at your option)
 * any later version
 *
 * This program is distributed in the hope that it will be entertaining,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILTY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#if ! defined(linux) && ! defined(NO_JOYSTICK)
#define NO_JOYSTICK
#endif

#include "xblast.h"
#include "x11_common.h"

#ifndef NO_JOYSTICK
#include <fcntl.h>
#include <unistd.h>
#include <linux/joystick.h>
#endif

#define JOYDIR_NONE  0x00
#define JOYDIR_UP    0x01
#define JOYDIR_DOWN  0x02
#define JOYDIR_Y     0x03
#define JOYDIR_LEFT  0x04
#define JOYDIR_RIGHT 0x08
#define JOYDIR_X     0x0C

#define THRESHOLD    8192

/*
 * local types
 */
typedef struct
{
	int fd;
	XBEventCode event;
	unsigned dir;
} XBJoystick;

/*
 * local variables
 */
#ifndef NO_JOYSTICK
static const char *joyDevice[NUM_JOYSTICKS] = {
	"/dev/js0",
	"/dev/js1",
	"/dev/input/js0",
	"/dev/input/js1",
	"/dev/input/js2",
	"/dev/input/js3",
};
static XBJoystick joystick[NUM_JOYSTICKS] = {
	{-1, XBE_NONE, 0},
	{-1, XBE_NONE, 0},
	{-1, XBE_NONE, 0},
	{-1, XBE_NONE, 0},
	{-1, XBE_NONE, 0},
	{-1, XBE_NONE, 0},
};
#endif

/*
 * global function: GUI_NumJoysticks
 * description:     Query number of connected joysticks
 * return value:    # joysticks
 */
int
GUI_NumJoysticks (void)
{
#ifndef NO_JOYSTICK
	size_t i, count;

	for (i = 0, count = 0; i < NUM_JOYSTICKS; i++) {
		if (joystick[i].fd != -1) {
			count++;
		}
	}
	return count;
#else
	return 0;
#endif
}								/* GUI_NumJoysticks */

/*
 * Initialize 
 */
XBBool
InitJoystick (void)
{
#ifndef NO_JOYSTICK
	size_t i, j;

	for (i = 0, j = 0; i < NUM_JOYSTICKS; i++) {
		if (-1 != (joystick[i].fd = open (joyDevice[i], O_RDONLY))) {
			RegisterJoystick (joystick[i].fd);
			joystick[i].event = XBE_JOYST_1 + j;
			Dbg_Out ("joystick %s initialised\n", joyDevice[i]);
			j++;
		}
		else {
			Dbg_Out ("joystick %s not initialised\n", joyDevice[i]);
		}
	}
#endif
	return XBTrue;
}								/* InitJoystick */

/*
 * shutdown
 */
void
FinishJoystick (void)
{
#ifndef NO_JOYSTICK
	size_t i;

	for (i = 0; i < NUM_JOYSTICKS; i++) {
		if (joystick[i].fd != -1) {
			close (joystick[i].fd);
		}
	}
#endif
}								/* FinishJoystick */

#ifndef NO_JOYSTICK
/*
 * calculate new joystick direction from axis-event
 */
static unsigned
EvalJoystickMove (unsigned dir, unsigned axis, int value)
{
	unsigned newDir = dir;

	switch (axis) {
		/* x-Axis */
	case 0:
		newDir &= ~JOYDIR_X;
		if (value > THRESHOLD) {
			newDir |= JOYDIR_RIGHT;
		}
		else if (value < -THRESHOLD) {
			newDir |= JOYDIR_LEFT;
		}
		break;
		/* y-axis */
	case 1:
		newDir &= ~JOYDIR_Y;
		if (value > THRESHOLD) {
			newDir |= JOYDIR_DOWN;
		}
		else if (value < -THRESHOLD) {
			newDir |= JOYDIR_UP;
		}
		break;
	default:
		break;
	}
#ifdef DEBUG_JOYSTICK
	Dbg_Out ("joy move: axis=%u value=%d => %02x\n", axis, value, newDir);
#endif
	return newDir;
}								/* EvalJoystickMove */
#endif

/*
 * handle new joystick event during game
 */
void
HandleXBlastJoystick (int fd)
{
#ifndef NO_JOYSTICK
	size_t i;
	struct js_event jsEvent;
	unsigned newDir;
	int value;

	/* find joystick */
	for (i = 0; i < NUM_JOYSTICKS; i++) {
		if (fd == joystick[i].fd) {
			break;
		}
	}
	if (i == NUM_JOYSTICKS) {
		return;
	}
	/* get event */
	if (sizeof (jsEvent) != read (fd, &jsEvent, sizeof (jsEvent))) {
		return;
	}
	/* check type */
	switch (jsEvent.type & ~JS_EVENT_INIT) {
		/* 
		 * handle button presses relases 
		 */
	case JS_EVENT_BUTTON:
		if (jsEvent.value) {
			switch (jsEvent.number) {
			case 0:
				QueueEventValue (joystick[i].event, XBGK_BOMB);
				break;
			case 1:
				QueueEventValue (joystick[i].event, XBGK_SPECIAL);
				break;
			default:
				break;
			}
		}
		break;
		/* 
		 *  handle direction changes 
		 */
	case JS_EVENT_AXIS:
		newDir = EvalJoystickMove (joystick[i].dir, jsEvent.number, jsEvent.value);
		switch (newDir) {
		case JOYDIR_UP:
			value = XBGK_GO_UP;
			break;
		case JOYDIR_DOWN:
			value = XBGK_GO_DOWN;
			break;
		case JOYDIR_LEFT:
			value = XBGK_GO_LEFT;
			break;
		case JOYDIR_RIGHT:
			value = XBGK_GO_RIGHT;
			break;
		case JOYDIR_NONE:
			value = XBGK_STOP_ALL;
			break;
		default:
			value = XBGK_NONE;
			break;
		}
		if (value != XBGK_NONE && newDir != joystick[i].dir) {
			QueueEventValue (joystick[i].event, value);
			joystick[i].dir = newDir;
		}
	}
#endif
}								/* HandleJoystick */

/*
 * handle new joystick event in menus
 */
void
HandleMenuJoystick (int fd)
{
#ifndef NO_JOYSTICK
	size_t i;
	struct js_event jsEvent;
	unsigned newDir;

	/* find joystick */
	for (i = 0; i < NUM_JOYSTICKS; i++) {
		if (fd == joystick[i].fd) {
			break;
		}
	}
	if (i == NUM_JOYSTICKS) {
		return;
	}
	/* get event */
	if (sizeof (jsEvent) != read (fd, &jsEvent, sizeof (jsEvent))) {
		return;
	}
	/* check type */
	switch (jsEvent.type & ~JS_EVENT_INIT) {
		/* 
		 * handle button presses relases 
		 */
	case JS_EVENT_BUTTON:
		if (jsEvent.value) {
			QueueEventValue (XBE_MENU, XBMK_SELECT);
		}
		break;
		/* 
		 *  handle direction changes 
		 */
	case JS_EVENT_AXIS:
		newDir = EvalJoystickMove (joystick[i].dir, jsEvent.number, jsEvent.value);
		/* test changes in y dir */
		if ((newDir & JOYDIR_Y) != (joystick[i].dir & JOYDIR_Y)) {
			switch (newDir & JOYDIR_Y) {
			case JOYDIR_UP:
				QueueEventValue (XBE_MENU, XBMK_UP);
				break;
			case JOYDIR_DOWN:
				QueueEventValue (XBE_MENU, XBMK_DOWN);
				break;
			default:
				break;
			}
		}
		/* test changes in x dir */
		if ((newDir & JOYDIR_X) != (joystick[i].dir & JOYDIR_X)) {
			switch (newDir & JOYDIR_X) {
			case JOYDIR_LEFT:
				QueueEventValue (XBE_MENU, XBMK_LEFT);
				break;
			case JOYDIR_RIGHT:
				QueueEventValue (XBE_MENU, XBMK_RIGHT);
				break;
			default:
				break;
			}
		}
		joystick[i].dir = newDir;
	}
#endif
}								/* HandleMenuJoystick */

/*
 * end of file x11_joystick.c
 */
