package engine;

import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

import engine.Game.STATE;
import hud.Map;
import hud.RightClickMenu;
import hud.SecondaryHUD;
import hud.inventory.Inventory;
import player.Player;
import player.PlayerAttackSequence;
import utils.Utils;
import worldGen.WorldGenerator;

/*  ------------- Nihilne ------------
    * Created by Mitch Weaver - 2f016 *
      ~~~~ All Rights Reserved ~~~~ */

@SuppressWarnings("serial") /* all the keybindings require this, really annoying */
final class KeyInput extends KeyAdapter {
	
	private static final Integer W=0, A=1, S=2, D=3, SPACE=4, TAB=5, _1=6, _2=7, _3=8, _4=9, _5=10, E=11, Q=12, ESCAPE=13, F=14,
			MINUS = 15, EQUALS = 16, CTRL = 17, PLUS=18;
	
	private static HashSet<Integer> keysDown;

	public static final void init() {
		if(keysDown == null) keysDown = new HashSet<Integer>(); else keysDown.clear();
		movementButtons();
		escape();
		Game.game.addKeyListener(new KeyInput()); /* for tab and other keys that are a pain with keybinds */
	}

	public final static void tick() {
		if(Player.health <= 0) return;
		
		if(Game.state == STATE.DEVELOPER_CONSOLE && !keysDown.isEmpty()) { keysDown.clear(); return; }
		
		if(keysDown.contains(TAB)){
			if(Game.state == STATE.GAME){
				Game.state = STATE.MAP;
				if(!Handler.creatingMap) Handler.getMapImage();
			} else if(Game.state == STATE.MAP){
				Map.clicked = Map.hasBeenClicked = false;
				Game.state = STATE.GAME;
			}
			keysDown.remove(TAB);
			Map.hasBeenClicked = false;
		}
		
		if (Game.state == STATE.GAME) {
			if(!WorldGenerator.WORLD_GENERATED) return;
			
			if (keysDown.contains(_1)) {
				SecondaryHUD.tab = SecondaryHUD.TAB.BACKPACK;
				Inventory.changeSettings();
			} else if (keysDown.contains(_2)) {
				SecondaryHUD.tab = SecondaryHUD.TAB.EQUIPMENT;
				Inventory.changeSettings();
			} else if (keysDown.contains(_3)) {
				SecondaryHUD.tab = SecondaryHUD.TAB.SKILLS;
				Inventory.changeSettings();
			} else if (keysDown.contains(_4)) {
				SecondaryHUD.tab = SecondaryHUD.TAB.MAGIC;
				Inventory.changeSettings();
			} else if (keysDown.contains(_5)) {
				SecondaryHUD.tab = SecondaryHUD.TAB.MINIMIZED;
				Inventory.changeSettings();
			}

			if(!RightClickMenu.OVERRIDE_SPACE) {
				if (keysDown.contains(SPACE)) {
					
					RightClickMenu.OVERRIDE_F = false;
					
					if(!Player.attacking) Player.using = true; 
				} else Player.using = false;
			}
			
			
			if(!RightClickMenu.OVERRIDE_F) {
				if (keysDown.contains(F)) {
					RightClickMenu.OVERRIDE_SPACE = false;
					
					if(!Player.using && !Player.attacking) PlayerAttackSequence.start();
				}
			}
			
			// ZOOMING WITH CTRL - + 
			
			if(keysDown.contains(CTRL)) {
				
				if(keysDown.contains(MINUS)) {
					if(Game.state == STATE.GAME) {
						Game.scaleFactor -= 0.25d;
						Game.scaleFactor = Utils.clamp(Game.scaleFactor, Game.MIN_SCALEFACTOR, Game.MAX_SCALEFACTOR);
					} else if (Game.state == STATE.MAP) Map.scaleFactor -= 0.25d;
					
					keysDown.remove(MINUS);
					
					// NOTE: ctrl makes player keep moving for some reason, this fixes but its kinda shitty
					// will need to find a work around later
					if(keysDown.contains(W)) keysDown.remove(W); if(keysDown.contains(A)) keysDown.remove(A);
					if(keysDown.contains(S)) keysDown.remove(S); if(keysDown.contains(D)) keysDown.remove(D);
				}
				
				else if(keysDown.contains(EQUALS) || keysDown.contains(PLUS)) {
					if(Game.state == STATE.GAME) {
						Game.scaleFactor += 0.25d;
						Game.scaleFactor = Utils.clamp(Game.scaleFactor, Game.MIN_SCALEFACTOR, Game.MAX_SCALEFACTOR);
					} else if (Game.state == STATE.MAP) Map.scaleFactor += 0.25d;
					
						 if(keysDown.contains(EQUALS)) keysDown.remove(EQUALS);
					else if(keysDown.contains(PLUS)) keysDown.remove(PLUS);
					
					if(keysDown.contains(W)) keysDown.remove(W); if(keysDown.contains(A)) keysDown.remove(A);
					if(keysDown.contains(S)) keysDown.remove(S); if(keysDown.contains(D)) keysDown.remove(D);
				}
			}
			
			// END ZOOMING WITH CTRL - + 
			

			// NOTE: These two timer values are given that the timer is set to ---> 28 <----
			if(!Player.attacking || PlayerAttackSequence.timer > 25 || PlayerAttackSequence.timer < 7) {
				if(keysDown.contains(W) && keysDown.contains(D)){
					Player.velX = Player.WALKING_SPEED;
					Player.velY = -Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				} else if(keysDown.contains(W) && keysDown.contains(A)){
					Player.velX = -Player.WALKING_SPEED;
					Player.velY = -Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				} else if(keysDown.contains(S) && keysDown.contains(D)){
					Player.velX = Player.WALKING_SPEED;
					Player.velY = Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				} else if(keysDown.contains(S) && keysDown.contains(A)){
					Player.velX = -Player.WALKING_SPEED;
					Player.velY = Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				} else if(keysDown.contains(W)){
					Player.velX = 0;
					Player.velY = -Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				}else if(keysDown.contains(S)){
					Player.velX = 0;
					Player.velY = Player.WALKING_SPEED;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				}else if(keysDown.contains(D)){
					Player.velX = Player.WALKING_SPEED;
					Player.velY = 0;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				}else if(keysDown.contains(A)){
					Player.velX = -Player.WALKING_SPEED;
					Player.velY = 0;
					Player.keepWalkingX = Player.keepWalkingY = false;
					RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
				}
	
					// Stop the player
					if (!Player.keepWalkingX && !Player.keepWalkingY)
						if (!keysDown.contains(W) && !keysDown.contains(A) && !keysDown.contains(S) && !keysDown.contains(D))
							Player.velX = Player.velY = 0;
					
					if (keysDown.contains(E)) {
						if (++Game.tempRotation >= 9) Game.tempRotation = 1;
						keysDown.remove(E);
					}
					if (keysDown.contains(Q)) {
						if (--Game.tempRotation < 1) Game.tempRotation = 8;
						keysDown.remove(Q);
					}
				} else Player.velX = Player.velY = 0;
				
				
				
			}
			
			if (keysDown.contains(ESCAPE)) {
				if (Game.state == STATE.GAME) {
					if (PauseMenu.pauseButtons.size() == 0) {
						PauseMenu.cleanUp();
						PauseMenu.init();
						Game.state = STATE.PAUSED;
						keysDown.remove(ESCAPE);
					}
				} else if (Game.state == STATE.PAUSED) {
					PauseMenu.cleanUp();
					Game.state = STATE.GAME;
					keysDown.remove(ESCAPE);
				}
				RightClickMenu.OVERRIDE_SPACE = RightClickMenu.OVERRIDE_F = false;
			}
		}

	private static final void movementButtons() {
		// Presses Walking
		final Action walkUp = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(W)) keysDown.add(W);
			}
		};

		final Action walkDown = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(S)) keysDown.add(S);
			}
		};

		final Action walkLeft = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(A)) keysDown.add(A);
			}
		};

		final Action walkRight = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(D)) keysDown.add(D);
			}
		};

		// Camera Rotation
		final Action rotateRight = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(E)) keysDown.add(E);
			}
		};
		final Action rotateLeft = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(Q)) keysDown.add(Q);
			}
		};

		// using (space)
		final Action using = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(SPACE)) keysDown.add(SPACE);
			}
		};
		
		// attacking
		final Action attacking = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(F)) keysDown.add(F);
			}
		};
		
		// 1 - 5 KEYS
		final Action _1 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(KeyInput._1)) keysDown.add(KeyInput._1);
			}
		};
		final Action _2 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(KeyInput._2)) keysDown.add(KeyInput._2);
			}
		};
		final Action _3 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(KeyInput._3)) keysDown.add(KeyInput._3);
			}
		};
		final Action _4 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(KeyInput._4)) keysDown.add(KeyInput._4);
			}
		};
		final Action _5 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (!keysDown.contains(KeyInput._5)) keysDown.add(KeyInput._5);
			}
		};

		// CTRL - + = SCROLLING
		final Action MINUS = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if(!keysDown.contains(KeyInput.MINUS)) keysDown.add(KeyInput.MINUS);
			}
		};
		final Action EQUALS = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if(!keysDown.contains(KeyInput.EQUALS)) keysDown.add(KeyInput.EQUALS);
			}
		};
		final Action CTRL = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if(!keysDown.contains(KeyInput.CTRL)) keysDown.add(KeyInput.CTRL);
			}
		};
		final Action PLUS = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if(!keysDown.contains(KeyInput.PLUS)) keysDown.add(KeyInput.PLUS);
			}
		};
		
		final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("W"), "walkUp");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("S"), "walkDown");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("A"), "walkLeft");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("D"), "walkRight");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("UP"), "walkUp");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("DOWN"), "walkDown");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("LEFT"), "walkLeft");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("RIGHT"), "walkRight");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("E"), "rotateRight");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("Q"), "rotateLeft");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("SPACE"), "using");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("F"), "attacking");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("1"), "1");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("2"), "2");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("3"), "3");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("4"), "4");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("5"), "5");
		Game.panel.getActionMap().put("walkUp", walkUp);
		Game.panel.getActionMap().put("walkDown", walkDown);
		Game.panel.getActionMap().put("walkLeft", walkLeft);
		Game.panel.getActionMap().put("walkRight", walkRight);
		Game.panel.getActionMap().put("rotateRight", rotateRight);
		Game.panel.getActionMap().put("rotateLeft", rotateLeft);
		Game.panel.getActionMap().put("using", using);
		Game.panel.getActionMap().put("attacking", attacking);
		Game.panel.getActionMap().put("1", _1);
		Game.panel.getActionMap().put("2", _2);
		Game.panel.getActionMap().put("3", _3);
		Game.panel.getActionMap().put("4", _4);
		Game.panel.getActionMap().put("5", _5);
		Game.panel.getActionMap().put("CTRL", CTRL);
		Game.panel.getActionMap().put("MINUS", MINUS);
		Game.panel.getActionMap().put("EQUALS", EQUALS);
		Game.panel.getActionMap().put("PLUS", PLUS);


		// Releases
		final Action stopWalkUp = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(W)) keysDown.remove(W);
			}
		};

		final Action stopWalkDown = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(S)) keysDown.remove(S);
			}
		};

		final Action stopWalkLeft = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(A)) keysDown.remove(A);
			}
		};

		final Action stopWalkRight = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(D)) keysDown.remove(D);
			}
		};

		final Action stopUsing = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(SPACE)) keysDown.remove(SPACE);
			}
		};
		
		final Action stopAttacking = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(F)) keysDown.remove(F);
			}
		};

		final Action release1 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(KeyInput._1)) keysDown.remove(KeyInput._1);
			}
		};
		final Action release2 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(KeyInput._2)) keysDown.remove(KeyInput._2);
			}
		};
		final Action release3 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(KeyInput._3)) keysDown.remove(KeyInput._3);
			}
		};
		final Action release4 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(KeyInput._4)) keysDown.remove(KeyInput._4);
			}
		};
		final Action release5 = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(KeyInput._5)) keysDown.remove(KeyInput._5);
			}
		};

		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released W"), "stopWalkUp");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released S"), "stopWalkDown");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released A"), "stopWalkLeft");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released D"), "stopWalkRight");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released UP"), "stopWalkUp");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released DOWN"), "stopWalkDown");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released LEFT"), "stopWalkLeft");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released RIGHT"), "stopWalkRight");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released SPACE"), "stopUsing");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released F"), "stopAttacking");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 1"), "release1");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 2"), "release2");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 3"), "release3");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 4"), "release4");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released 5"), "release5");
		Game.panel.getActionMap().put("stopWalkUp", stopWalkUp);
		Game.panel.getActionMap().put("stopWalkDown", stopWalkDown);
		Game.panel.getActionMap().put("stopWalkLeft", stopWalkLeft);
		Game.panel.getActionMap().put("stopWalkRight", stopWalkRight);
		Game.panel.getActionMap().put("stopUsing", stopUsing);
		Game.panel.getActionMap().put("stopAttacking", stopAttacking);
		Game.panel.getActionMap().put("release1", release1);
		Game.panel.getActionMap().put("release2", release2);
		Game.panel.getActionMap().put("release3", release3);
		Game.panel.getActionMap().put("release4", release4);
		Game.panel.getActionMap().put("release5", release5);
	}

	private static final void escape() {
		final int when = JComponent.WHEN_IN_FOCUSED_WINDOW;
		final Action escape = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if(Game.state != STATE.DEVELOPER_CONSOLE) if (!keysDown.contains(ESCAPE)) keysDown.add(ESCAPE);
			}
		};
		
		final Action releasedESCAPE = new AbstractAction() {
			public void actionPerformed(ActionEvent e) {
				if (keysDown.contains(ESCAPE)) keysDown.remove(ESCAPE);
			}
		};

		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
		Game.panel.getInputMap(when).put(KeyStroke.getKeyStroke("released ESCAPE"), "released ESCAPE");
		Game.panel.getActionMap().put("released ESCAPE", releasedESCAPE);
		Game.panel.getActionMap().put("escape", escape);
	}
	
	// BOOLEANS TO STOP CONSTANT FIRING OF KEYS
	private static boolean tabDown;
	
	public final void keyPressed(KeyEvent e){
		if(Game.state == STATE.DEVELOPER_CONSOLE) return;
		
		else if(e.getKeyCode() == KeyEvent.VK_CONTROL && !keysDown.contains(CTRL)) keysDown.add(CTRL);
		else if(e.getKeyCode() == KeyEvent.VK_MINUS && !keysDown.contains(MINUS)) keysDown.add(MINUS);
		else if(e.getKeyCode() == KeyEvent.VK_EQUALS && !keysDown.contains(EQUALS)) { keysDown.add(EQUALS); } 
		else if(e.getKeyCode() == KeyEvent.VK_PLUS && !keysDown.contains(PLUS)) { keysDown.add(PLUS); } 
		
		else if(!tabDown && e.getKeyCode() == KeyEvent.VK_TAB && !keysDown.contains(TAB)){
			keysDown.add(TAB);
			tabDown = true;
		}
	}
	
	public final void keyReleased(KeyEvent e){
		if(tabDown && e.getKeyCode() == KeyEvent.VK_TAB) tabDown = false;
		
		else if(e.getKeyCode() == KeyEvent.VK_CONTROL && keysDown.contains(CTRL)) keysDown.remove(CTRL);
		else if(e.getKeyCode() == KeyEvent.VK_MINUS && keysDown.contains(MINUS)) keysDown.remove(MINUS);
		else if(e.getKeyCode() == KeyEvent.VK_EQUALS && keysDown.contains(EQUALS)) keysDown.remove(EQUALS);
		else if(e.getKeyCode() == KeyEvent.VK_PLUS && keysDown.contains(PLUS)) keysDown.remove(PLUS);
	}
	
	public static final synchronized void cleanUp(){ keysDown.clear(); keysDown = null; }
}
