package com.badlogic.gdx.controllers;

import com.badlogic.gdx.Application;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.LifecycleListener;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectMap;

import org.robovm.apple.foundation.Foundation;
import org.robovm.apple.foundation.NSArray;
import org.robovm.apple.gamecontroller.GCController;
import org.robovm.apple.uikit.UIKeyCommand;
import org.robovm.apple.uikit.UIKeyModifierFlags;
import org.robovm.apple.uikit.UIViewController;
import org.robovm.objc.Selector;
import org.robovm.objc.block.VoidBlock1;

public class IosControllerManager implements ControllerManager {
	private final Array<Controller> controllers = new Array<>();
	private final Array<ControllerListener> listeners = new Array<>();
	private boolean initialized = false;
	private ICadeController iCadeController;

	public IosControllerManager() {
	}

	/**
	 * you need to call this method to register IosControllerManager with libGDX before your first call to
	 * Controllers.getControllers().
	 */
	public static void initializeIosControllers() {
		ObjectMap<Application, ControllerManager> managers = Controllers.managers;

		// this is a copy from Controllers class. A hack to get IosControllerManager to work with libGDX
		if (Foundation.getMajorSystemVersion() < 7) {
			Gdx.app.log("Controllers", "IosControllerManager not added, needs iOS 7+.");
		} else if (!managers.containsKey(Gdx.app)) {
			IosControllerManager manager = new IosControllerManager();

			managers.put(Gdx.app, manager);
			final Application app = Gdx.app;
			Gdx.app.addLifecycleListener(new LifecycleListener() {
				public void resume() {
				}

				public void pause() {
				}

				public void dispose() {
					Controllers.managers.remove(app);
					Gdx.app.log("Controllers", "removed manager for application, " + Controllers.managers.size + " managers active");
				}
			});
			Gdx.app.log("Controllers", "added manager for application, " + managers.size + " managers active");

		} else {
			Gdx.app.log("Controllers", "IosControllerManager not added, manager already active. ");
		}
	}

	public static void enableICade(UIViewController controller, Selector action) {
		for (int i = 0; i < ICadeController.KEYS_TO_HANDLE.length(); i++) {
			controller.addKeyCommand(new UIKeyCommand(Character.toString(ICadeController.KEYS_TO_HANDLE.charAt(i)),
					UIKeyModifierFlags.None, action));
		}

		controller.becomeFirstResponder();
		Gdx.app.log("Controllers", "iCade support activated");
	}

	public static void keyPress(UIKeyCommand sender) {
		//a key for ICadeController was pressed
		// instantiate it, if not already available
		IosControllerManager controllerManager = (IosControllerManager) Controllers.managers.get(Gdx.app);

		if (controllerManager != null)
			controllerManager.handleKeyPressed(sender);
	}

	private void handleKeyPressed(UIKeyCommand sender) {
		if (iCadeController == null) {
			Gdx.app.log("Controllers", "iCade key was pressed, adding iCade controller.");

			iCadeController = new ICadeController();
			controllers.add(iCadeController);

			synchronized (listeners) {
				for (ControllerListener listener : listeners)
					listener.connected(iCadeController);
			}
		}

		iCadeController.handleKeyPressed(sender.getInput());
	}

	protected boolean isSupportedController(GCController controller) {
		return controller.getExtendedGamepad() != null || controller.getGamepad() != null;
	}

	@Override
	public Array<Controller> getControllers() {
		initializeControllerArray();

		return controllers;
	}

	private void initializeControllerArray() {
		if (!initialized) {
			initialized = true;

			NSArray<GCController> controllers = GCController.getControllers();

			for (GCController controller : controllers) {
				if (isSupportedController(controller))
					this.controllers.add(new IosController(controller));
			}

			GCController.Notifications.observeDidConnect(new VoidBlock1<GCController>() {
				@Override
				public void invoke(GCController gcController) {
					onControllerConnect(gcController);
				}
			});

			GCController.Notifications.observeDidDisconnect(new VoidBlock1<GCController>() {
				@Override
				public void invoke(GCController gcController) {
					onControllerDisconnect(gcController);
				}
			});

		}
	}

	protected void onControllerConnect(GCController gcController) {
		if (!isSupportedController(gcController))
			return;

		boolean alreadyInList = false;
		for (Controller controller : controllers) {
			if (controller instanceof IosController && ((IosController) controller).getController() == gcController) {
				alreadyInList = true;
				break;
			}
		}

		if (!alreadyInList) {
			IosController iosController = new IosController(gcController);
			controllers.add(iosController);

			synchronized (listeners) {
				for (ControllerListener listener : listeners)
					listener.connected(iosController);
			}
		}
	}

	protected void onControllerDisconnect(GCController gcController) {
		IosController oldReference = null;
		for (Controller controller : controllers) {
			if (controller instanceof IosController && ((IosController) controller).getController() == gcController) {
				oldReference = (IosController) controller;
			}
		}

		if (oldReference != null) {
			controllers.removeValue(oldReference, true);

			synchronized (listeners) {
				for (ControllerListener listener : listeners)
					listener.disconnected(oldReference);
			}

			oldReference.dispose();
		}
	}

	@Override
	public void addListener(ControllerListener controllerListener) {
		initializeControllerArray();

		synchronized (listeners) {
			if (!listeners.contains(controllerListener, true))
				listeners.add(controllerListener);
		}
	}

	@Override
	public void removeListener(ControllerListener controllerListener) {
		synchronized (listeners) {
			listeners.removeValue(controllerListener, true);
		}
	}

	@Override
	public Array<ControllerListener> getListeners() {
		return listeners;
	}

	@Override
	public void clearListeners() {
		synchronized (listeners) {
			listeners.clear();
		}
	}
}