/* Anodyne SDL Joystick Config Detector Thing
 * Written by Ethan "flibitijibibo" Lee
 * http://www.flibitijibibo.com/
 *
 * Released under public domain.
 * No warranty implied; use at your own risk.
 *
 * Directions:
 * 1. gcc AnodyneJoystickDetector.c `sdl-config --libs`
 * 2. ./a.out
 * 3. ?????
 * 4. PROFIT!!!
 */

#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_main.h>

int main(int argc, char* argv[])
{
	// Game input values
	int numBindings = 7;
	int anodyneBindings[numBindings];
	int anodyneJoystickDirection[numBindings];
  int is_hat[numBindings]; // Is the nth input a hat? etc
  int is_axis[numBindings];
  int is_button[numBindings];

  int iter = 0;
  for (iter = 0; iter < 7; iter++)  {
    is_hat[iter] = 0;
    is_axis[iter] = 0;
    is_button[iter] = 0;
  }

	int curBinding = 0;
	const char* const bindingNames[] =
	{
		"Move Up",
		"Move Down",
		"Move Left",
		"Move Right",
		"Attack,Confirm",
		"???,Cancel",
		"Menu"
	};

  const char* const keyNames[] =
  {
    "Up", "Down", "Left", "Right", "c", "x", "KP_Enter"
  };

	// Initialize SDL, set up filler video mode.
	SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
	SDL_SetVideoMode(1, 1, 32, 0);

	// Open the first joystick.
	SDL_Joystick *theDevice = SDL_JoystickOpen(0);

	// Just in case...
	if (!SDL_JoystickOpened(0))
	{
		printf("Joystick failed to open! Exiting...\n");
		SDL_Quit();
		return 0;
	}

	// Gather our information about the opened joystick.
	int numAxes = SDL_JoystickNumAxes(theDevice);
	int numHats = SDL_JoystickNumHats(theDevice) * 4; // Up/Down/Left/Right
	int numButtons = SDL_JoystickNumButtons(theDevice);

	// Print the joystick information, including the device name.
	printf("Gamepad: %s\n", SDL_JoystickName(0));
	printf("\tNumber of axes: %d\n", numAxes);
	printf("\tNumber of hats: %d\n", numHats);
	printf("\tNumber of buttons: %d\n", numButtons);

	/* The actual joystick query loop. 
	 * Listens for joystick actions and prints the Anodyne config value.
	 * Note that repeating the same joystick value does not repeat output!
	 */
	SDL_Event evt;
	int newQuery = 1;
	// We cache these to avoid repeating value prints.
	int cachedAxis = -1, cachedAxisDirection = -1;
	int cachedHat = -1, cachedHatDir = -1;
	int cachedButton = -1;
	for (SDL_PollEvent(&evt); evt.type != SDL_QUIT; SDL_PollEvent(&evt))
	{
		// Print message only if we've not already asked!
		if (newQuery == 1)
		{
			if (curBinding >= numBindings)
			{
				break; // We got the last one.
			}
			newQuery = 0;
			printf(
				"\nAwaiting new joystick input for %s\n",
				bindingNames[curBinding]
			);
		}

		// Don't even bother with other stuff.
		if (	evt.type != SDL_JOYAXISMOTION &&
			evt.type != SDL_JOYHATMOTION &&
			evt.type != SDL_JOYBUTTONDOWN
		) continue;

		// Axis check
		if (evt.type == SDL_JOYAXISMOTION)
		{
			// Check against cache.
			if (	evt.jaxis.axis == cachedAxis &&
				(evt.jaxis.value > 0) == cachedAxisDirection)
				continue;

			// We do this to avoid excess fighting between axes.
			if (abs(evt.jaxis.value) < 16384)
				continue;

			// And we do this to prevent spam from idle analog
			// buttons (such as Sixaxis) - Thanks John Drinkwater!
			if (abs(evt.jaxis.value) == 32768)
				continue;

			// Print our result.
			printf(
				"\nAxis #%d, Positive %d\n",
				evt.jaxis.axis,
				(evt.jaxis.value > 0)
			);

			// Bind it.
      is_axis[curBinding] = 1;
			anodyneBindings[curBinding] = evt.jaxis.axis;
			anodyneJoystickDirection[curBinding] =
				(evt.jaxis.value > 0);
			curBinding++;

			// Cache values.
			cachedAxis = evt.jaxis.axis;
			cachedAxisDirection = (evt.jaxis.value > 0);

			// Now we can ask for a new value.
			newQuery = 1;
		}
		// Hat check
		else if (evt.type == SDL_JOYHATMOTION)
		{
			// Check the direction.
			char* direction;
			int dirOffset;
			if (evt.jhat.value & SDL_HAT_UP)
			{
        // Note diroffsets...might be wrong, oh well
				direction = "Up";
				dirOffset = 2;
			}
			else if (evt.jhat.value & SDL_HAT_DOWN)
			{
				direction = "Down";
				dirOffset = 3;
			}
			else if (evt.jhat.value & SDL_HAT_LEFT)
			{
				direction = "Left";
				dirOffset = 0;
			}
			else if (evt.jhat.value & SDL_HAT_RIGHT)
			{
				direction = "Right";
				dirOffset = 1;
			}
			else // lolwut
				continue;

			// Check against cache.
			if (	evt.jhat.hat == cachedHat &&
				(evt.jhat.hat + dirOffset) == cachedHatDir
			) continue;

			// Print our result.
			printf(
				"\nHat #%d, Direction %s\n",
				evt.jhat.hat,
				direction
			);

			// Bind it.
      is_hat[curBinding] = 1;
			anodyneBindings[curBinding] = evt.jhat.hat;
			anodyneJoystickDirection[curBinding] =
				dirOffset;
			curBinding++;

			// Cache values.
			cachedHat = evt.jhat.hat;
			cachedHatDir = evt.jhat.hat + dirOffset;

			// Now we can ask for a new value.
			newQuery = 1;
		}
		// Button check
		else if (evt.type == SDL_JOYBUTTONDOWN)
		{
			// Check against cache.
			if (evt.jbutton.button == cachedButton)
				continue;

			// Print our result.
			printf(
				"\nButton #%d\n",
				evt.jbutton.button
			);

			// Bind it.
      is_button[curBinding] = 1;
			anodyneBindings[curBinding] = evt.jbutton.button;
			curBinding++;

			// Cache values.
			cachedButton = evt.jbutton.button;

			// Now we can ask for a new value.
			newQuery = 1;
		}
	}

	// Print the values we got from the query.
	printf("\nWe got these values:\n");
	for (curBinding = 0; curBinding < numBindings; curBinding++)
	{
		printf("%s: ", bindingNames[curBinding]);
		if (1 == is_axis[curBinding])
		{
			printf(
				"Axis #%d, Positive: %d\n",
				anodyneBindings[curBinding],
				anodyneJoystickDirection[curBinding]
			);
    }
    else if (1 == is_hat[curBinding])
		{
			printf(
				"Hat #%d, ",
				anodyneBindings[curBinding]
			);
			if (anodyneJoystickDirection[curBinding] == 0)
			{
				printf("Up\n");
			}
			else if (anodyneJoystickDirection[curBinding] == 1)
			{
				printf("Down\n");
			}
			else if (anodyneJoystickDirection[curBinding] == 2)
			{
				printf("Left\n");
			}

			else if (anodyneJoystickDirection[curBinding] == 3)
			{
				printf("Right\n");
			}
		}
		else
		{
			printf("Button #%d\n", anodyneBindings[curBinding]);
		}
	}

  printf("\nConfig:\n");

  char * thres_str = calloc(1,1024*8); // hope this is big enough :) 
  strcat(thres_str,"START Anodyne\n");
  strcat(thres_str,"-thresh");
  for (iter = 0; iter < numHats/2 + numAxes; iter++) { // each pair gets 2 vals
    strcat(thres_str," -16383 16383");
  }
  strcat(thres_str,"\n-X\n-buttons");

  int j = 0;
  int found_btn = 0;
  // UDLR12P
  for (iter = 0; iter < numButtons; iter++) {
    strcat(thres_str," ");
    for (j = 0; j < 7; j++) {
      if (is_button[j] == 1 && anodyneBindings[j] == iter) {
        strcat(thres_str,keyNames[j]);
        found_btn = 1;
        break;
      }


    }

    if (found_btn) {
      found_btn = 0;
    } else {
      strcat(thres_str,"a");
    }
  }

  found_btn = 0;
  strcat(thres_str,"\n-axis");
	//			anodyneBindings[curBinding],
	///			anodyneJoystickDirection[curBinding]
  for (iter = 0; iter < numHats + 2*numAxes; iter++) {
    strcat(thres_str," ");
    // Do axes
    if (iter < 2*numAxes) {
      for (j = 0; j < 7; j++) {
        if (is_axis[j] == 1 && anodyneBindings[j] == (iter/2)) {
          if (iter % 2 == 0 && anodyneJoystickDirection[j] == 0) { // Neg or pos?
            strcat(thres_str,keyNames[j]);
            found_btn = 1;
          } else if (iter % 2 == 1 && anodyneJoystickDirection[j] == 1) {
            strcat(thres_str,keyNames[j]);
            found_btn = 1;
          }
        }
      }
    // Do hats
    } else {
      for (j = 0; j < 7; j++) {
        if (is_hat[j] == 1 && anodyneBindings[j] == (iter - 2*numAxes)/4) {
          if (iter % 4 == anodyneJoystickDirection[j]) {
            strcat(thres_str,keyNames[j]);
            found_btn = 1;
          }
        }
      }
    }
    if (found_btn == 1) {
      found_btn = 0;
    } else {
      strcat(thres_str,"a");
    }
  }
  

  printf("%s\n",thres_str);

  FILE * f = fopen("anodyne.j2kconfig","w");
  fwrite(thres_str,strlen(thres_str),1,f);
  fclose(f);
  printf("Config written to anodyne.j2kconfig!\n");

	// Clean up and get out.
	printf("Goodbye!\n");
	SDL_JoystickClose(theDevice);
	SDL_Quit();
	return 0;
}
