/*
Wii Nunchuck/Wii Classic/SNES+NES Classic:
      LOOKING AT THE PLUG ON FRONT OF CONSOLE/BACK OF WIIMOTE (not coming from controller)

       |---------------|
       |   1   3   5   |
       |   o   o   o   |
       |   o   o   o   |
       |   2   4   6   |
       |   |-------|   |
       |---|       |---|

   PIN # USAGE (colors from my extension cable, check your own)

  1:  VCC 3.3V ONLY      - white
  2:  SCL                - yellow
  3:  3.3V SENSE, unused - red
  4:  unused             -
  5:  SDA                - green
  6:  GND                - black
*/

#include <Arduino.h>

// we only support 1 pad here
#define GAMEPAD_COUNT 1

#define AXIS_CENTER_IN 126
#define AXIS_MAX_IN 230
#define AXIS_MIN_IN 15

#define TRIGGER_MAX_IN 255
#define TRIGGER_MIN_IN 40

#include <NintendoExtensionCtrl.h>

#include "gamepad/Gamepad.h"
#include "util.cpp"

GAMEPAD_CLASS gamepad;

ExtensionPort port;	 // Port for communicating with extension controllers

Nunchuk::Shared nchuk(port);			  // Read Nunchuk formatted data from the port
ClassicController::Shared classic(port);  // Read Classic Controller formatted data from the port

ExtensionController* controllers[] = {
	// Array of available controllers, for controller-specific init
	&nchuk,
	&classic,
};

const int NumControllers = sizeof(controllers) / sizeof(ExtensionController*);	// # of controllers, auto-generated

void (*controllerChanged)();

const uint8_t c = 0;  // for now just do 1 pad

void nunchuckChanged() {
	//nchuk.printDebug(); return;
	gamepad.buttons(c, 0);
	if (nchuk.buttonC()) {
		gamepad.press(c, BUTTON_A);
	}
	if (nchuk.buttonZ()) {
		gamepad.press(c, BUTTON_B);
	}
	// todo: anything with roll/pitch/accel ?
	gamepad.setAxis(c, translateAxis(nchuk.joyX()), -translateAxis(nchuk.joyY()), 0, 0, 0, 0, DPAD_CENTER);
}

void classicChanged() {
	//classic.printDebug(); return;
	gamepad.buttons(c, 0);
	if (classic.buttonA()) {
		gamepad.press(c, BUTTON_A);
	}
	if (classic.buttonB()) {
		gamepad.press(c, BUTTON_B);
	}
	if (classic.buttonY()) {
		gamepad.press(c, BUTTON_Y);
	}
	if (classic.buttonX()) {
		gamepad.press(c, BUTTON_X);
	}
	if (classic.buttonZL()) {
		gamepad.press(c, BUTTON_L);
	}
	if (classic.buttonZR()) {
		gamepad.press(c, BUTTON_R);
	}
	if (classic.buttonL()) {
		gamepad.press(c, BUTTON_TL2);
	}
	if (classic.buttonR()) {
		gamepad.press(c, BUTTON_TR2);
	}
	if (classic.buttonPlus()) {
		gamepad.press(c, BUTTON_PLUS);
	}
	if (classic.buttonMinus()) {
		gamepad.press(c, BUTTON_MINUS);
	}
	if (classic.buttonHome()) {
		gamepad.press(c, BUTTON_HOME);
	}
	auto hat = calculateDpadDirection(classic.dpadUp(), classic.dpadDown(), classic.dpadLeft(), classic.dpadRight());
	gamepad.setAxis(c,
					translateAxis(classic.leftJoyX()),
					-translateAxis(classic.leftJoyY()),
					translateAxis(classic.rightJoyX()),
					-translateAxis(classic.rightJoyY()),
					translateTrigger(classic.triggerL()),
					translateTrigger(classic.triggerR()),
					hat);
}

boolean connectController() {
	boolean connected = port.connect();	 // Connect to the controller

	if (connected == true) {
		for (int i = 0; i < NumControllers; i++) {
			if (controllers[i]->controllerTypeMatches()) {	 // If this controller is connected...
				connected = controllers[i]->specificInit();	 // ...run the controller-specific initialization
				if (connected == true) {
					ExtensionType conType = port.getControllerType();
					switch (conType) {
						case (ExtensionType::Nunchuk):
							controllerChanged = nunchuckChanged;
							break;
						case (ExtensionType::ClassicController):
							controllerChanged = classicChanged;
							break;
						default:
							//Serial.println("Other controller connected!");
							return false;
					}
					return true;
				}
			}
		}
	}

	return connected;
}

void setup() {
	gamepad.begin();
	port.begin();  // init I2C

	while (!connectController()) {
		//Serial.println("No controller found!");
		delay(1000);
	}
}

void loop() {
	boolean success = port.update();  // Get new data from the controller

	if (success == true) {	// We've got data!
		// todo: only call this if data changed?
		controllerChanged();
	} else {  // Data is bad :(
		while (!connectController()) {
			//Serial.println("Controller Disconnected!");
			delay(1000);
		}
	}
}
