/*
 *  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 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY 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.
 */

#include "port.h"
#include SDL_INCLUDE(SDL.h)
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "vcontrol.h"
#include "libs/memlib.h"
#include "keynames.h"
#include "libs/log.h"
#include "libs/reslib.h"

/* How many binding slots are allocated at once. */
#define POOL_CHUNK_SIZE 64

/* Total number of key input buckets. SDL1 keys are a simple enum,
 * but SDL2 scatters key symbols through the entire 32-bit space,
 * so we do not rely on being able to declare an array with one
 * entry per key. */
#define KEYBOARD_INPUT_BUCKETS 512

typedef struct vcontrol_keybinding {
	int *target;
	sdl_key_t keycode;
	struct vcontrol_keypool *parent;
	struct vcontrol_keybinding *next;
} keybinding;

typedef struct vcontrol_keypool {
	keybinding pool[POOL_CHUNK_SIZE];
	int remaining;
	struct vcontrol_keypool *next;
} keypool;


#ifdef HAVE_JOYSTICK

typedef struct vcontrol_joystick_axis {
	keybinding *neg, *pos;
	int polarity;
} axis_type;

typedef struct vcontrol_joystick_hat {
	keybinding *left, *right, *up, *down;
	Uint8 last;
} hat_type;

typedef struct vcontrol_joystick {
	SDL_Joystick *stick;
	int numaxes, numbuttons, numhats;
	int threshold;
	axis_type *axes;
	keybinding **buttons;
	hat_type *hats;
} joystick;

static joystick *joysticks;

#endif /* HAVE_JOYSTICK */

static unsigned int joycount;
static keybinding *bindings[KEYBOARD_INPUT_BUCKETS];

static keypool *pool;

/* Last interesting event */
static int event_ready;
static SDL_Event last_interesting;

static keypool *
allocate_key_chunk (void)
{
	keypool *x = HMalloc (sizeof (keypool));
	if (x)
	{
		int i;
		x->remaining = POOL_CHUNK_SIZE;
		x->next = NULL;
		for (i = 0; i < POOL_CHUNK_SIZE; i++)
		{
			x->pool[i].target = NULL;
			x->pool[i].keycode = SDLK_UNKNOWN;
			x->pool[i].next = NULL;
			x->pool[i].parent = x;
		}
	}
	return x;
}

static void
free_key_pool (keypool *x)
{
	if (x)
	{
		free_key_pool (x->next);
		HFree (x);
	}
}

#ifdef HAVE_JOYSTICK

static void
create_joystick (int index)
{
	SDL_Joystick *stick;
	int axes, buttons, hats;
	if ((unsigned int) index >= joycount)
	{
		log_add (log_Warning, "VControl warning: Tried to open a non-existent joystick!");
		return;
	}
	if (joysticks[index].stick)
	{
		// Joystick is already created.  Return.
		return;
	}
	stick = SDL_JoystickOpen (index);
	if (stick)
	{
		joystick *x = &joysticks[index];
		int j;
#if SDL_MAJOR_VERSION == 1
		log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (index));
#else
		log_add (log_Info, "VControl opened joystick: %s", SDL_JoystickName (stick));
#endif
		axes = SDL_JoystickNumAxes (stick);
		buttons = SDL_JoystickNumButtons (stick);
		hats = SDL_JoystickNumHats (stick);
		log_add (log_Info, "%d axes, %d buttons, %d hats.", axes, buttons, hats);
		x->numaxes = axes;
		x->numbuttons = buttons;
		x->numhats = hats;
		x->axes = HMalloc (sizeof (axis_type) * axes);
		x->buttons = HMalloc (sizeof (keybinding *) * buttons);
		x->hats = HMalloc (sizeof (hat_type) * hats);
		for (j = 0; j < axes; j++)
		{
			x->axes[j].neg = x->axes[j].pos = NULL;
		}
		for (j = 0; j < hats; j++)
		{
			x->hats[j].left = x->hats[j].right = NULL;
			x->hats[j].up = x->hats[j].down = NULL;
			x->hats[j].last = SDL_HAT_CENTERED;
		}
		for (j = 0; j < buttons; j++)
		{
			x->buttons[j] = NULL;
		}
		x->stick = stick;
	}
	else
	{
		log_add (log_Warning, "VControl: Could not initialize joystick #%d", index);
	}
}
			
static void
destroy_joystick (int index)
{
	SDL_Joystick *stick = joysticks[index].stick;
	if (stick)
	{
		SDL_JoystickClose (stick);
		joysticks[index].stick = NULL;
		HFree (joysticks[index].axes);
		HFree (joysticks[index].buttons);
		HFree (joysticks[index].hats);
		joysticks[index].numaxes = joysticks[index].numbuttons = 0;
		joysticks[index].axes = NULL;
		joysticks[index].buttons = NULL;
		joysticks[index].hats = NULL;
	}
}

#endif /* HAVE_JOYSTICK */

static void
key_init (void)
{
	unsigned int i;
	int num_keys; // Temp to match type of param for SDL_GetKeyState().
	pool = allocate_key_chunk ();
	for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++)
		bindings[i] = NULL;

#ifdef HAVE_JOYSTICK
	/* Prepare for possible joystick controls.  We don't actually
	   GRAB joysticks unless we're asked to make a joystick
	   binding, though. */
	joycount = SDL_NumJoysticks ();
	if (joycount)
	{
		joysticks = HMalloc (sizeof (joystick) * joycount);
		for (i = 0; i < joycount; i++)
		{
			joysticks[i].stick = NULL;	
			joysticks[i].numaxes = joysticks[i].numbuttons = 0;
			joysticks[i].axes = NULL;
			joysticks[i].buttons = NULL;
			joysticks[i].threshold = 10000;
		}
	}
	else
	{
		joysticks = NULL;
	}
#else
	joycount = 0;
#endif /* HAVE_JOYSTICK */
}

static void
key_uninit (void)
{
	unsigned int i;
	free_key_pool (pool);
	for (i = 0; i < KEYBOARD_INPUT_BUCKETS; i++)
		bindings[i] = NULL;
	pool = NULL;

#ifdef HAVE_JOYSTICK
	for (i = 0; i < joycount; i++)
		destroy_joystick (i);
	HFree (joysticks);
#endif /* HAVE_JOYSTICK */
}

void
VControl_Init (void)
{
	key_init ();
}

void
VControl_Uninit (void)
{
	key_uninit ();
}

int
VControl_SetJoyThreshold (int port, int threshold)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joysticks[port].threshold = threshold;
		return 0;
	}
	else
#else
	(void) port;
	(void) threshold;
#endif /* HAVE_JOYSTICK */
	{
		// log_add (log_Warning, "VControl_SetJoyThreshold passed illegal port %d", port);
		return -1;
	}
}


static void
add_binding (keybinding **newptr, int *target, sdl_key_t keycode)
{
	keybinding *newbinding;
	keypool *searchbase;
	int i;

	/* Acquire a pointer to the keybinding * that we'll be
	 * overwriting.  Along the way, ensure we haven't already
	 * bound this symbol to this target.  If we have, return.*/
	while (*newptr != NULL)
	{
		if (((*newptr)->target == target) && ((*newptr)->keycode == keycode))
		{
			return;
		}
		newptr = &((*newptr)->next);
	}

	/* Now hunt through the binding pool for a free binding. */

	/* First, find a chunk with free spots in it */

	searchbase = pool;
	while (searchbase->remaining == 0)
	{
		/* If we're completely full, allocate a new chunk */
		if (searchbase->next == NULL)
		{
			searchbase->next = allocate_key_chunk ();
		}
		searchbase = searchbase->next;
	}

	/* Now find a free binding within it */

	newbinding = NULL;
	for (i = 0; i < POOL_CHUNK_SIZE; i++)
	{
		if (searchbase->pool[i].target == NULL)
		{
			newbinding = &searchbase->pool[i];
			break;
		}
	}

	/* Sanity check. */
	if (!newbinding)
	{
		log_add (log_Warning, "add_binding failed to find a free binding slot!");
		return;
	}

	newbinding->target = target;
	newbinding->keycode = keycode;
	newbinding->next = NULL;
	*newptr = newbinding;
	searchbase->remaining--;
}

static void
remove_binding (keybinding **ptr, int *target, sdl_key_t keycode)
{
	if (!(*ptr))
	{
		/* Nothing bound to symbol; return. */
		return;
	}
	else if (((*ptr)->target == target) && ((*ptr)->keycode == keycode))
	{
		keybinding *todel = *ptr;
		*ptr = todel->next;
		todel->target = NULL;
		todel->keycode = SDLK_UNKNOWN;
		todel->next = NULL;
		todel->parent->remaining++;
	}
	else
	{
		keybinding *prev = *ptr;
		while (prev && prev->next != NULL)
		{
			if (prev->next->target == target)
			{
				keybinding *todel = prev->next;
				prev->next = todel->next;
				todel->target = NULL;
				todel->keycode = SDLK_UNKNOWN;
				todel->next = NULL;
				todel->parent->remaining++;
			}
			prev = prev->next;
		}
	}
}

static void
activate (keybinding *i, sdl_key_t keycode)
{
	while (i != NULL)
	{
		if (i->keycode == keycode)
		{
			*(i->target) = (*(i->target)+1) | VCONTROL_STARTBIT;
		}
		i = i->next;
	}
}

static void
deactivate (keybinding *i, sdl_key_t keycode)
{
	while (i != NULL)
	{
		int v = *(i->target) & VCONTROL_MASK;
		if ((i->keycode == keycode) && (v > 0))
		{
			*(i->target) = (v-1) | (*(i->target) & VCONTROL_STARTBIT);
		}
		i = i->next;
	}
}

static void
event2gesture (SDL_Event *e, VCONTROL_GESTURE *g)
{
	switch (e->type)
	{
	case SDL_KEYDOWN:
		g->type = VCONTROL_KEY;
		g->gesture.key = e->key.keysym.sym;
		break;
	case SDL_JOYAXISMOTION:
		g->type = VCONTROL_JOYAXIS;
		g->gesture.axis.port = e->jaxis.which;
		g->gesture.axis.index = e->jaxis.axis;
		g->gesture.axis.polarity = (e->jaxis.value < 0) ? -1 : 1;
		break;
	case SDL_JOYHATMOTION:
		g->type = VCONTROL_JOYHAT;
		g->gesture.hat.port = e->jhat.which;
		g->gesture.hat.index = e->jhat.hat;
		g->gesture.hat.dir = e->jhat.value;
		break;
	case SDL_JOYBUTTONDOWN:
		g->type = VCONTROL_JOYBUTTON;
		g->gesture.button.port = e->jbutton.which;
		g->gesture.button.index = e->jbutton.button;
		break;

	default:
		g->type = VCONTROL_NONE;
		break;
	}
}

int
VControl_AddGestureBinding (VCONTROL_GESTURE *g, int *target)
{
	int result = -1;
	switch (g->type)
	{
	case VCONTROL_KEY:
		result = VControl_AddKeyBinding (g->gesture.key, target);
		break;

	case VCONTROL_JOYAXIS:
#ifdef HAVE_JOYSTICK
		result = VControl_AddJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target);
		break;
#endif
	case VCONTROL_JOYHAT:
#ifdef HAVE_JOYSTICK
		result = VControl_AddJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target);
		break;
#endif
	case VCONTROL_JOYBUTTON:
#ifdef HAVE_JOYSTICK
		result = VControl_AddJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target);
		break;
#endif /* HAVE_JOYSTICK */
	case VCONTROL_NONE:
		/* Do nothing */
		break;

	default:
		log_add (log_Warning, "VControl_AddGestureBinding didn't understand argument gesture");
		result = -1;
		break;
	}
	return result;
}

void
VControl_RemoveGestureBinding (VCONTROL_GESTURE *g, int *target)
{
	switch (g->type)
	{
	case VCONTROL_KEY:
		VControl_RemoveKeyBinding (g->gesture.key, target);
		break;

	case VCONTROL_JOYAXIS:
#ifdef HAVE_JOYSTICK
		VControl_RemoveJoyAxisBinding (g->gesture.axis.port, g->gesture.axis.index, (g->gesture.axis.polarity < 0) ? -1 : 1, target);
		break;
#endif /* HAVE_JOYSTICK */
	case VCONTROL_JOYHAT:
#ifdef HAVE_JOYSTICK
		VControl_RemoveJoyHatBinding (g->gesture.hat.port, g->gesture.hat.index, g->gesture.hat.dir, target);
		break;
#endif /* HAVE_JOYSTICK */
	case VCONTROL_JOYBUTTON:
#ifdef HAVE_JOYSTICK
		VControl_RemoveJoyButtonBinding (g->gesture.button.port, g->gesture.button.index, target);
		break;
#endif /* HAVE_JOYSTICK */
	case VCONTROL_NONE:
		break;
	default:
		log_add (log_Warning, "VControl_RemoveGestureBinding didn't understand argument gesture");
		break;
	}
}

int
VControl_AddKeyBinding (sdl_key_t symbol, int *target)
{
	add_binding(&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol);
	return 0;
}

void
VControl_RemoveKeyBinding (sdl_key_t symbol, int *target)
{
	remove_binding (&bindings[symbol % KEYBOARD_INPUT_BUCKETS], target, symbol);
}

int
VControl_AddJoyAxisBinding (int port, int axis, int polarity, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((axis >= 0) && (axis < j->numaxes))
		{
			if (polarity < 0)
			{
				add_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN);
			}
			else if (polarity > 0)
			{
				add_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN);
			}
			else
			{
				log_add (log_Debug, "VControl: Attempted to bind to polarity zero");
				return -1;
			}
		}
		else
		{
			// log_add (log_Debug, "VControl: Attempted to bind to illegal axis %d", axis);
			return -1;
		}
	}
	else
#else
	(void) port;
	(void) axis;
	(void) polarity;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		// log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
		return -1;
	}
	return 0;
}

void
VControl_RemoveJoyAxisBinding (int port, int axis, int polarity, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((axis >= 0) && (axis < j->numaxes))
		{
			if (polarity < 0)
			{
				remove_binding(&joysticks[port].axes[axis].neg, target, SDLK_UNKNOWN);
			}
			else if (polarity > 0)
			{
				remove_binding(&joysticks[port].axes[axis].pos, target, SDLK_UNKNOWN);
			}
			else
			{
				log_add (log_Debug, "VControl: Attempted to unbind from polarity zero");
			}
		}
		else
		{
			log_add (log_Debug, "VControl: Attempted to unbind from illegal axis %d", axis);
		}
	}
	else
#else
	(void) port;
	(void) axis;
	(void) polarity;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
	}
}

int
VControl_AddJoyButtonBinding (int port, int button, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((button >= 0) && (button < j->numbuttons))
		{
			add_binding(&joysticks[port].buttons[button], target, SDLK_UNKNOWN);
			return 0;
		}
		else
		{
			// log_add (log_Debug, "VControl: Attempted to bind to illegal button %d", button);
			return -1;
		}
	}
	else
#else
	(void) port;
	(void) button;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		// log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
		return -1;
	}
}

void
VControl_RemoveJoyButtonBinding (int port, int button, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((button >= 0) && (button < j->numbuttons))
		{
			remove_binding (&joysticks[port].buttons[button], target, SDLK_UNKNOWN);
		}
		else
		{
			log_add (log_Debug, "VControl: Attempted to unbind from illegal button %d", button);
		}
	}
	else
#else
	(void) port;
	(void) button;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
	}
}

int
VControl_AddJoyHatBinding (int port, int which, Uint8 dir, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((which >= 0) && (which < j->numhats))
		{
			if (dir == SDL_HAT_LEFT)
			{
				add_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_RIGHT)
			{
				add_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_UP)
			{
				add_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_DOWN)
			{
				add_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN);
			}
			else
			{
				// log_add (log_Debug, "VControl: Attempted to bind to illegal direction");
				return -1;
			}
			return 0;
		}
		else
		{
			// log_add (log_Debug, "VControl: Attempted to bind to illegal hat %d", which);
			return -1;
		}
	}
	else
#else
	(void) port;
	(void) which;
	(void) dir;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		// log_add (log_Debug, "VControl: Attempted to bind to illegal port %d", port);
		return -1;
	}
}

void
VControl_RemoveJoyHatBinding (int port, int which, Uint8 dir, int *target)
{
#ifdef HAVE_JOYSTICK
	if (port >= 0 && (unsigned int) port < joycount)
	{
		joystick *j = &joysticks[port];
		if (!(j->stick))
			create_joystick (port);
		if ((which >= 0) && (which < j->numhats))
		{
			if (dir == SDL_HAT_LEFT)
			{
				remove_binding(&joysticks[port].hats[which].left, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_RIGHT)
			{
				remove_binding(&joysticks[port].hats[which].right, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_UP)
			{
				remove_binding(&joysticks[port].hats[which].up, target, SDLK_UNKNOWN);
			}
			else if (dir == SDL_HAT_DOWN)
			{
				remove_binding(&joysticks[port].hats[which].down, target, SDLK_UNKNOWN);
			}
			else
			{
				log_add (log_Debug, "VControl: Attempted to unbind from illegal direction");
			}
		}
		else
		{
			log_add (log_Debug, "VControl: Attempted to unbind from illegal hat %d", which);
		}
	}
	else
#else
	(void) port;
	(void) which;
	(void) dir;
	(void) target;
#endif /* HAVE_JOYSTICK */
	{
		log_add (log_Debug, "VControl: Attempted to unbind from illegal port %d", port);
	}
}

void
VControl_RemoveAllBindings (void)
{
	key_uninit ();
	key_init ();
}

void
VControl_ProcessKeyDown (sdl_key_t symbol)
{
	activate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol);
}

void
VControl_ProcessKeyUp (sdl_key_t symbol)
{
	deactivate (bindings[symbol % KEYBOARD_INPUT_BUCKETS], symbol);
}

void
VControl_ProcessJoyButtonDown (int port, int button)
{
#ifdef HAVE_JOYSTICK
	if (!joysticks[port].stick)
		return;
	activate (joysticks[port].buttons[button], SDLK_UNKNOWN);
#else
	(void) port;
	(void) button;
#endif /* HAVE_JOYSTICK */
}

void
VControl_ProcessJoyButtonUp (int port, int button)
{
#ifdef HAVE_JOYSTICK
	if (!joysticks[port].stick)
		return;
	deactivate (joysticks[port].buttons[button], SDLK_UNKNOWN);
#else
	(void) port;
	(void) button;
#endif /* HAVE_JOYSTICK */
}

void
VControl_ProcessJoyAxis (int port, int axis, int value)
{
#ifdef HAVE_JOYSTICK
	int t;
	if (!joysticks[port].stick)
		return;
	t = joysticks[port].threshold;
	if (value > t)
	{
		if (joysticks[port].axes[axis].polarity != 1)
		{
			if (joysticks[port].axes[axis].polarity == -1)
			{
				deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
			}
			joysticks[port].axes[axis].polarity = 1;
			activate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
		}
	}
	else if (value < -t)
	{
		if (joysticks[port].axes[axis].polarity != -1)
		{
			if (joysticks[port].axes[axis].polarity == 1)
			{
				deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
			}
			joysticks[port].axes[axis].polarity = -1;
			activate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
		}
	}
	else
	{
		if (joysticks[port].axes[axis].polarity == -1)
		{
			deactivate (joysticks[port].axes[axis].neg, SDLK_UNKNOWN);
		}
		else if (joysticks[port].axes[axis].polarity == 1)
		{
			deactivate (joysticks[port].axes[axis].pos, SDLK_UNKNOWN);
		}
		joysticks[port].axes[axis].polarity = 0;
	}
#else
	(void) port;
	(void) axis;
	(void) value;
#endif /* HAVE_JOYSTICK */
}

void
VControl_ProcessJoyHat (int port, int which, Uint8 value)
{
#ifdef HAVE_JOYSTICK
	Uint8 old;
	if (!joysticks[port].stick)
		return;
	old = joysticks[port].hats[which].last;
	if (!(old & SDL_HAT_LEFT) && (value & SDL_HAT_LEFT))
		activate (joysticks[port].hats[which].left, SDLK_UNKNOWN);
	if (!(old & SDL_HAT_RIGHT) && (value & SDL_HAT_RIGHT))
		activate (joysticks[port].hats[which].right, SDLK_UNKNOWN);
	if (!(old & SDL_HAT_UP) && (value & SDL_HAT_UP))
		activate (joysticks[port].hats[which].up, SDLK_UNKNOWN);
	if (!(old & SDL_HAT_DOWN) && (value & SDL_HAT_DOWN))
		activate (joysticks[port].hats[which].down, SDLK_UNKNOWN);
	if ((old & SDL_HAT_LEFT) && !(value & SDL_HAT_LEFT))
		deactivate (joysticks[port].hats[which].left, SDLK_UNKNOWN);
	if ((old & SDL_HAT_RIGHT) && !(value & SDL_HAT_RIGHT))
		deactivate (joysticks[port].hats[which].right, SDLK_UNKNOWN);
	if ((old & SDL_HAT_UP) && !(value & SDL_HAT_UP))
		deactivate (joysticks[port].hats[which].up, SDLK_UNKNOWN);
	if ((old & SDL_HAT_DOWN) && !(value & SDL_HAT_DOWN))
		deactivate (joysticks[port].hats[which].down, SDLK_UNKNOWN);
	joysticks[port].hats[which].last = value;
#else
	(void) port;
	(void) which;
	(void) value;
#endif /* HAVE_JOYSTICK */
}

void
VControl_ResetInput (void)
{
	/* Step through every valid entry in the binding pool and zero
	 * them out.  This will probably zero entries multiple times;
	 * oh well, no harm done. */

	keypool *base = pool;
	while (base != NULL)
	{
		int i;
		for (i = 0; i < POOL_CHUNK_SIZE; i++)
		{
			if(base->pool[i].target)
			{
				*(base->pool[i].target) = 0;
			}
		}
		base = base->next;
	}
}

void
VControl_BeginFrame (void)
{
	/* Step through every valid entry in the binding pool and zero
	 * out the frame-start bit.  This will probably zero entries
	 * multiple times; oh well, no harm done. */

	keypool *base = pool;
	while (base != NULL)
	{
		int i;
		for (i = 0; i < POOL_CHUNK_SIZE; i++)
		{
			if(base->pool[i].target)
			{
				*(base->pool[i].target) &= VCONTROL_MASK;
			}
		}
		base = base->next;
	}
}

void
VControl_HandleEvent (const SDL_Event *e)
{
	switch (e->type)
	{
		case SDL_KEYDOWN:
#if SDL_MAJOR_VERSION > 1
			if (!e->key.repeat)
#endif
			{
				VControl_ProcessKeyDown (e->key.keysym.sym);
				last_interesting = *e;
				event_ready = 1;
			}
			break;
		case SDL_KEYUP:
			VControl_ProcessKeyUp (e->key.keysym.sym);
			break;

#ifdef HAVE_JOYSTICK
		case SDL_JOYAXISMOTION:
			VControl_ProcessJoyAxis (e->jaxis.which, e->jaxis.axis, e->jaxis.value);
			if ((e->jaxis.value > 15000) || (e->jaxis.value < -15000))
			{
				last_interesting = *e;
				event_ready = 1;
			}
			break;
		case SDL_JOYHATMOTION:
			VControl_ProcessJoyHat (e->jhat.which, e->jhat.hat, e->jhat.value);
			last_interesting = *e;
			event_ready = 1;
			break;
		case SDL_JOYBUTTONDOWN:
			VControl_ProcessJoyButtonDown (e->jbutton.which, e->jbutton.button);
			last_interesting = *e;
			event_ready = 1;
			break;
		case SDL_JOYBUTTONUP:
			VControl_ProcessJoyButtonUp (e->jbutton.which, e->jbutton.button);
			break;
#endif /* HAVE_JOYSTICK */

		default:
			break;
	}
}

/* Tracking the last interesting event */

void
VControl_ClearGesture (void)
{
	event_ready = 0;
}

int
VControl_GetLastGesture (VCONTROL_GESTURE *g)
{
	if (event_ready && g != NULL)
	{
		event2gesture(&last_interesting, g);
	}
	return event_ready;
}		

/* Configuration file grammar is as follows:  One command per line, 
 * hashes introduce comments that persist to end of line.  Blank lines
 * are ignored.
 *
 * Terminals are represented here as quoted strings, e.g. "foo" for 
 * the literal string foo.  These are matched case-insensitively.
 * Special terminals are:
 *
 * KEYNAME:  This names a key, as defined in keynames.c.
 * IDNAME:   This is an arbitrary string of alphanumerics, 
 *           case-insensitive, and ending with a colon.  This
 *           names an application-specific control value.
 * NUM:      This is an unsigned integer.
 * EOF:      End of file
 *
 * Nonterminals (the grammar itself) have the following productions:
 * 
 * configline <- IDNAME binding
 *             | "joystick" NUM "threshold" NUM
 *             | "version" NUM
 *
 * binding    <- "key" KEYNAME
 *             | "joystick" NUM joybinding
 *
 * joybinding <- "axis" NUM polarity
 *             | "button" NUM
 *             | "hat" NUM direction
 *
 * polarity   <- "positive" | "negative"
 *
 * dir        <- "up" | "down" | "left" | "right"
 *
 * This grammar is amenable to simple recursive descent parsing;
 * in fact, it's fully LL(1). */

/* Actual maximum line and token sizes are two less than this, since
 * we need space for the \n\0 at the end */
#define LINE_SIZE 256
#define TOKEN_SIZE 64

typedef struct vcontrol_parse_state {
	char line[LINE_SIZE];
	char token[TOKEN_SIZE];
	int index;
	int error;
	int linenum;
} parse_state;

static void
next_token (parse_state *state)
{
	int index, base;

	state->token[0] = 0;
	/* skip preceding whitespace */
	base = state->index;
	while (state->line[base] && isspace (state->line[base]))
	{
		base++;
	}

	index = 0;
	while (index < (TOKEN_SIZE-1) && state->line[base+index] && !isspace (state->line[base+index]))
	{
		state->token[index] = state->line[base+index];
		index++;
	}
	state->token[index] = 0;

	/* If the token was too long, skip ahead until we get to whitespace */
	while (state->line[base+index] && !isspace (state->line[base+index]))
	{
		index++;
	}

	state->index = base+index;
}

static void
expected_error (parse_state *state, const char *expected)
{
	log_add (log_Warning, "VControl: Expected '%s' on config file line %d",
			expected, state->linenum);
	state->error = 1;
}

static void
consume (parse_state *state, const char *expected)
{
	if (strcasecmp (expected, state->token))
	{
		expected_error (state, expected);
	}
	next_token (state);
}

static int
consume_keyname (parse_state *state)
{
	int keysym = VControl_name2code (state->token);
	if (!keysym)
	{
		log_add (log_Warning, "VControl: Illegal key name '%s' on config file line %d",
				state->token, state->linenum);
		state->error = 1;
	}
	next_token (state);
	return keysym;
}

static int
consume_num (parse_state *state)
{
	char *end;
	int result = strtol (state->token, &end, 10);
	if (*end != '\0')
	{
		log_add (log_Warning, "VControl: Expected integer on config line %d",
				state->linenum);
		state->error = 1;
	}
	next_token (state);
	return result;
}

static int
consume_polarity (parse_state *state)
{
	int result = 0;
	if (!strcasecmp (state->token, "positive"))
	{
		result = 1;
	}
	else if (!strcasecmp (state->token, "negative"))
	{
		result = -1;
	}
	else
	{
		expected_error (state, "positive' or 'negative");
	}
	next_token (state);
	return result;
}

static Uint8
consume_dir (parse_state *state)
{
	Uint8 result = 0;
	if (!strcasecmp (state->token, "left"))
	{
		result = SDL_HAT_LEFT;
	}
	else if (!strcasecmp (state->token, "right"))
	{
		result = SDL_HAT_RIGHT;
	}
	else if (!strcasecmp (state->token, "up"))
	{
		result = SDL_HAT_UP;
	}
	else if (!strcasecmp (state->token, "down"))
	{
		result = SDL_HAT_DOWN;
	}
	else
	{
		expected_error (state, "left', 'right', 'up' or 'down");
	}
	next_token (state);
	return result;
}

static void
parse_joybinding (parse_state *state, VCONTROL_GESTURE *gesture)
{
	int sticknum;
	consume (state, "joystick");
	sticknum = consume_num (state);
	if (!state->error)
	{
		if (!strcasecmp (state->token, "axis"))
		{
			int axisnum;
			consume (state, "axis");
			axisnum = consume_num (state);
			if (!state->error)
			{
				int polarity = consume_polarity (state);
				if (!state->error)
				{
					gesture->type = VCONTROL_JOYAXIS;
					gesture->gesture.axis.port = sticknum;
					gesture->gesture.axis.index = axisnum;
					gesture->gesture.axis.polarity = polarity;
				}
			}
		} 
		else if (!strcasecmp (state->token, "button"))
		{
			int buttonnum;
			consume (state, "button");
			buttonnum = consume_num (state);
			if (!state->error)
			{
				gesture->type = VCONTROL_JOYBUTTON;
				gesture->gesture.button.port = sticknum;
				gesture->gesture.button.index = buttonnum;
			}
		}
		else if (!strcasecmp (state->token, "hat"))
		{
			int hatnum;
			consume (state, "hat");
			hatnum = consume_num (state);
			if (!state->error)
			{
				Uint8 dir = consume_dir (state);
				if (!state->error)
				{
					gesture->type = VCONTROL_JOYHAT;
					gesture->gesture.hat.port = sticknum;
					gesture->gesture.hat.index = hatnum;
					gesture->gesture.hat.dir = dir;
				}
			}
		}
		else
		{
			expected_error (state, "axis', 'button', or 'hat");
		}
	}
}

static void
parse_gesture (parse_state *state, VCONTROL_GESTURE *gesture)
{
	gesture->type = VCONTROL_NONE; /* Default to error */
	if (!strcasecmp (state->token, "key"))
	{
		/* Parse key binding */
		int keysym;
		consume (state, "key");
		keysym = consume_keyname (state);
		if (!state->error)
		{
			gesture->type = VCONTROL_KEY;
			gesture->gesture.key = keysym;
		}
	}
	else if (!strcasecmp (state->token, "joystick"))
	{
		parse_joybinding (state, gesture);
	}
	else
	{
		expected_error (state, "key' or 'joystick");
	}
}

void
VControl_ParseGesture (VCONTROL_GESTURE *g, const char *spec)
{
	parse_state ps;

	strncpy (ps.line, spec, LINE_SIZE);
	ps.line[LINE_SIZE - 1] = '\0';
	ps.index = ps.error = 0;
	ps.linenum = -1;

	next_token (&ps);
	parse_gesture (&ps, g);
	if (ps.error)
		printf ("Error parsing %s\n", spec);
}

int
VControl_DumpGesture (char *buf, int n, VCONTROL_GESTURE *g)
{
	switch (g->type) 
	{
	case VCONTROL_KEY:
		return snprintf (buf, n, "key %s", VControl_code2name (g->gesture.key));
	case VCONTROL_JOYAXIS:
		return snprintf (buf, n, "joystick %d axis %d %s", g->gesture.axis.port, g->gesture.axis.index, 
				(g->gesture.axis.polarity > 0) ? "positive" : "negative");
	case VCONTROL_JOYBUTTON:
		return snprintf (buf, n, "joystick %d button %d", g->gesture.button.port, g->gesture.button.index);
	case VCONTROL_JOYHAT:
		return snprintf (buf, n, "joystick %d hat %d %s", g->gesture.hat.port, g->gesture.hat.index, 
				(g->gesture.hat.dir == SDL_HAT_UP) ? "up" :
				((g->gesture.hat.dir == SDL_HAT_DOWN) ? "down" : 
				((g->gesture.hat.dir == SDL_HAT_LEFT) ? "left" : "right")));
	default:
		buf[0] = '\0';
		return 0;
	}
}
