package projectzombie.input;

import static projectzombie.input.GameInput.activateItem_last;
import static projectzombie.input.GameInput.activate_last;
import static projectzombie.input.GameInput.backButton_last;
import static projectzombie.input.GameInput.dropItem_last;
import static projectzombie.input.GameInput.hotbar_l;
import static projectzombie.input.GameInput.hotbar_r;
import static projectzombie.input.GameInput.moveDown;
import static projectzombie.input.GameInput.moveUp;
import static projectzombie.input.GameInput.move_last;
import static projectzombie.input.GameInput.startButton_last;

import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;

import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWJoystickCallbackI;

import mainloop.task.IMainloopTask;
import projectzombie.Main;
import projectzombie.input.types.Input;

public class JoystickCallback implements GLFWJoystickCallbackI, IMainloopTask
{
	public static final JoystickCallback JOYSTICK_CALLBACK = new JoystickCallback();
	private ArrayList<Integer> connections = new ArrayList<Integer>();

	@Override
	public void invoke(int jid, int event)
	{
		// Gamepad connect
		if(event == GLFW.GLFW_CONNECTED)
		{
			// Log the event and add the connection
			System.out.println("Gamepad "+jid+" connected");
			connections.add(jid);
		}
		
		// Gamepad disconnect
		else if(event == GLFW.GLFW_DISCONNECTED)
		{
			// Log the event and remove the connection
			System.out.println("Gamepad "+jid+" disconnected");
			connections.remove((Object) jid);
		}
	}
	
	public void init()
	{
		// Loop over all the gamepads
		for(int i=0;i<=GLFW.GLFW_JOYSTICK_LAST;i++)
		{
			// Is this gamepad present; add the gamepad
			if(GLFW.glfwJoystickPresent(i)) {
				if(GLFW.glfwJoystickIsGamepad(i)) {
					System.out.println("Gamepad "+i+" connected");
					connections.add(i);
				}
			}
		}
	}

	@Override
	public boolean MainLoopDelay(long millis) {
		return millis > Main.tickrate;
	}

	@Override
	public boolean MainLoopRepeat() {
		return true;
	}
	
	private float combineJoystickAxis(float a, float b)
	{
		if(b > 0.3 || b < -0.3) a += b;
		return a;
	}

	@Override
	public void MainLoopUpdate()
	{
		// Gamepad properties
		float left_x = 0;
		float left_y = 0;
		float left_trigger = 0;
		float right_x = 0;
		float right_y = 0;
		float right_trigger = 0;
		boolean left_stick_button = false;
		boolean right_stick_button = false;
		boolean button_a = false;
		boolean button_b = false;
		boolean button_x = false;
		boolean button_y = false;
		boolean button_start = false;
		boolean button_back = false;
		boolean button_home = false;
		boolean dpad_up = false;
		boolean dpad_down = false;
		boolean dpad_left = false;
		boolean dpad_right = false;
		boolean shoulder_left = false;
		boolean shoulder_right = false;
		
		int jid2 = 0;
		
		try {
			// Loop over all the connected gamepads
			for(int jid : connections)
			{
				// Get all the axes
				jid2 = jid;
				FloatBuffer axes = GLFW.glfwGetJoystickAxes(jid);
				
				// Store all the axes data
				left_x = combineJoystickAxis(left_x, axes.get(0));
				left_y = combineJoystickAxis(left_y, axes.get(1));
				right_x = combineJoystickAxis(right_x, axes.get(3));
				right_y = combineJoystickAxis(right_y, axes.get(4));
				left_trigger = combineJoystickAxis(left_trigger, axes.get(2));
				right_trigger = combineJoystickAxis(right_trigger, axes.get(5));
				
				// Get all the buttons
				ByteBuffer buttons = GLFW.glfwGetJoystickButtons(jid);
				
				// Store all the button data
				button_a = buttons.get(0) == 1 || button_a;
				button_b = buttons.get(1) == 1 || button_b;
				button_x = buttons.get(2) == 1 || button_x;
				button_y = buttons.get(3) == 1 || button_y;
				shoulder_left = buttons.get(4) == 1 || shoulder_left;
				shoulder_right = buttons.get(5) == 1 || shoulder_right;
				button_back = buttons.get(6) == 1 || button_back;
				button_start = buttons.get(7) == 1 || button_start;
				button_home = buttons.get(8) == 1 || button_home;
				left_stick_button = buttons.get(9) == 1 || left_stick_button;
				right_stick_button = buttons.get(10) == 1 || right_stick_button;
				dpad_left = buttons.get(11) == 1 || dpad_left;
				dpad_right = buttons.get(12) == 1 || dpad_right;
				dpad_up = buttons.get(13) == 1 || dpad_up;
				dpad_down = buttons.get(14) == 1 || dpad_down;
			}
		}
		
		catch(Exception e) {
			connections.remove((Object) jid2);
			System.err.println("Removed controller "+jid2+" due to "+e.toString());
		}
		
		Input input = Main.menu.input;
		
		// Is the left stick moved into a position (movement stick)
		if((left_x > 0.3 || left_x < -0.3 || left_y > 0.3 || left_y < -0.3) && !moveUp && !moveDown)
		{
			// Get the the angle
			double angle = Math.toDegrees(Math.atan2(left_y, left_x)) + 90;
			input.move(true, angle);
			move_last = true;
			
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
		}
		
		// Set the players moving to false
		else if(move_last && !moveUp && !moveDown) {
			move_last = false;
			input.move(false, 0);
		}
		
		// Is the right x axis stick moved into a position (camera stick)
		if(right_x > 0.3 || right_x < -0.3) {
			input.camera(true, right_x);
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
		}
		
		// Gun trigger
		if(right_trigger > 0.3) {
			input.fire(true);
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
		}
		
		// Item trigger
		if(left_trigger > 0.3) {
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
			input.itemAction(true);
		}
		
		else {
			activateItem_last = false;
		}
		
		if(shoulder_left) {
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
			if(!hotbar_l) {
				hotbar_l = true;
				input.hotbarShift(true, -1);
			}
		}
		
		else if(hotbar_l) {
			hotbar_l = false;
		}
		
		if(shoulder_right) {
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
			if(!hotbar_r) {
				hotbar_r = true;
				input.hotbarShift(true, 1);
			}
		}
		
		else if(hotbar_r) {
			hotbar_r = false;
		}
		
		// Activate button (A Button)
		if(button_a) {
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
			if(!activate_last) {
				input.activate(true);
				activate_last = true;
			}
		}
		
		else if(activate_last) {
			activate_last = false;
			input.activate(false);
		}
		
		// Drop item
		if(button_b) {
			InputMode.Controller = true;
			Main.window.setMouseVisibility(false);
			if(!dropItem_last) {
				input.itemDrop(true);
				dropItem_last = true;
			}
		}
		
		else if(dropItem_last) {
			dropItem_last = false;
		}
		
		// Back button
		if(button_b) {
			if(!backButton_last) {
				input.back(true);
				backButton_last = true;
			}
		}
		
		else if(backButton_last) {
			backButton_last = false;
		}
		
		// Pause the game
		if(button_start) {
			Main.window.setMouseVisibility(false);
			InputMode.Controller = true;
			if(!startButton_last) {
				startButton_last = true;
				input.pause(true);
			}
		}
		
		else if(startButton_last) {
			startButton_last = false;
		}
	}

}
