package engine;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.LinkedList;

import engine.Game.STATE;
import hud.inventory.InvenHelper;
import hud.inventory.Inventory;
import interfaces.Biomes;
import interfaces.Colors;
import interfaces.ItemIDs;
import interfaces.Mob;
import interfaces.Python;
import mobs.Goblin;
import mobs.Rabbit;
import player.Player;
import structures.Fire;
import utils.Circle;
import utils.Point2f;
import utils.Utils;
import world.Boulder;
import world.Flower;
import world.Sapling;
import world.TallGrass;
import world.Tile;
import world.Tree;

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

public final class DeveloperConsole extends KeyAdapter implements Colors, ItemIDs, Biomes, Python {
	
	private static StringBuilder sb;
	private static LinkedList<String> history;
	private static int index;
	private static STATE tempState;
	
	public static synchronized final void init() {
		/* COMMENT THIS IN THE FINAL RELEASE */
		sb = new StringBuilder();
		history = new LinkedList<String>();
		Game.game.addKeyListener(new DeveloperConsole());
	}
	
	public final void keyPressed(final KeyEvent e) {
		
		if(e.getKeyCode() == KeyEvent.VK_ESCAPE && Game.state == STATE.DEVELOPER_CONSOLE) { exit(); return; }
		else if(e.getKeyChar() == '~') { if(Game.state != STATE.DEVELOPER_CONSOLE) open();  else exit(); return; }
		else if(Game.state != STATE.DEVELOPER_CONSOLE) return;
		
		else if(e.getKeyCode() == KeyEvent.VK_UP) {
			if(history.size() <= 0) return;
			final String temp = sb.toString();
			sb = new StringBuilder();
			try { sb.append(history.get(index--)); } catch (Exception ex) {
				index++;
				sb.append(temp);
			}
		}
		
		else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			if(history.size() <= 0) return;
			final String temp = sb.toString();
			sb = new StringBuilder();
			try { sb.append(history.get(++index)); } catch (Exception ex) {
				index--;
				sb.append(temp);
			}
		}
		
		else if(e.getKeyCode() == KeyEvent.VK_ENTER) {
			history.add(sb.toString());
			execute(sb.toString());
			sb = new StringBuilder();
			return;
		}
		
		else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) { if(sb.length() > 0) sb.deleteCharAt(sb.length()-1); return; }
		
		else if(sb.length() < 67) {
			switch(e.getKeyCode()) {
				case KeyEvent.VK_BACK_SPACE: return;
				case KeyEvent.VK_TAB: return;
				case KeyEvent.VK_SHIFT: return;
				case KeyEvent.VK_UP: return;
				case KeyEvent.VK_DOWN: return;
				case KeyEvent.VK_LEFT: return;
				case KeyEvent.VK_RIGHT: return;
				case KeyEvent.VK_CONTROL: return;
				case KeyEvent.VK_WINDOWS: return;
				case KeyEvent.VK_UNDEFINED: return;
				case KeyEvent.VK_NUM_LOCK: return;
				default: sb.append(e.getKeyChar());
			}
		}
	}
	
	private static synchronized final void execute(final String command) {
		if(command.length() <= 0) { exit(); return; }
		
		String action, item, value;
		
		// GET THE ACTION, IF THE ACTION IS A ONE-WORD PHRASE, ACTION = THE WHOLE COMMAND
		try { action = command.substring(0, command.indexOf(" ")); } catch (StringIndexOutOfBoundsException e) { action = command.toString(); }
		
		// GET THE ITEM, IF THERE IS NO AMOUNT, ITEM = JUST THE ITEM, IF THERE IS NO ITEM, ITEM = ""
		try { final String tempItem = command.substring(command.indexOf(" ") + 1); item = tempItem.substring(0, tempItem.indexOf(" ")); } catch (StringIndexOutOfBoundsException e) { 
				try { item = command.substring(command.indexOf(" ") + 1); } catch (StringIndexOutOfBoundsException ee) { item = ""; }
		}
		
		//  GET THE VALUE ---- 
		try { final String temp = command.substring(command.indexOf(" ") + 1)  ; 
		      value = temp.substring(temp.indexOf(" ") + 1);
		} catch (StringIndexOutOfBoundsException ee) { value = ""; }
		
		item.replace(" ", ""); action.replace(" ", ""); value.replace(" ", "");
		
// -------------------------------------------------------------------------------------------------------------------------------------//
		OUTER_SWITCH:
		switch(action) {
			
			case "exec_script": pi.execfile(item); break;
			case "exec": pi.exec(item); break;
			
			case "health": Player.health = Integer.parseInt(value); break;
			
			case "clear_mobs": Handler.mobs.clear(); break;
			
			case "garbage_collect": System.gc(); break;
			case "clear_history": history.clear(); break;
			
			case "unlimit_zoom": { Game.MIN_SCALEFACTOR = 0.01d; Game.MAX_SCALEFACTOR = 100; break; }
			
			case "starter_kit": {
				Inventory.addItem(LOGS, 20);
				Inventory.addItem(PICKED_GRASS, 40);
				Inventory.addItem(STICKS, 20);
				Inventory.addItem(ROCKS, 10);
				Inventory.addItem(TORCH);
				Inventory.addItem(CRUDE_PICKAXE);
				Inventory.addItem(CRUDE_AXE); break;
			}
			
			case "speed": { Player.WALKING_SPEED = Float.parseFloat(item); break; }
			case "clock_speed": { Game.clockSpeed = Integer.parseInt(item); break; }
			case "nuke":  {
				for(Mob m : Handler.mobs)         if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.health = 0;
				for(Tree m : Handler.trees)       if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.health = 0;
				for(Boulder m : Handler.boulders) if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.health = 0;
				
				for(Flower m : Handler.flowers)   if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.alive = false;
				for(TallGrass m : Handler.grass)  if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.alive = false;
				for(Sapling m : Handler.saplings) if(Handler.AREA_RECT.contains(m.center.x, m.center.y)) m.alive = false;
				break;
			}
			case "clear_all_items": Handler.droppedItems.clear(); break;
			case "drop_all_items": InvenHelper.DROP_ALL_INVENTORY(); break;
			case "discover_all":  Tile.DISCOVER_ALL(); break; 
			case "undiscover_all": Tile.UN_DISCOVER_ALL(); break;
			case "paint_map": Handler.getMapImage(); break;
			
			///////////// DEBUG BOOLEAN FLIPPING ///////////////////////
			case "draw_mob_attack_bounds": Game.drawMobAttackBounds = !Game.drawMobAttackBounds; break;
			case "draw_collision_bounds": Game.drawCollisionBounds = !Game.drawCollisionBounds; break;
			case "draw_mob_territories": Game.drawMobTerritoryZones = !Game.drawMobTerritoryZones; break;
			case "draw_mob_aggro_radii": Game.drawMobAggroRadii = !Game.drawMobAggroRadii; break;
			case "draw_resource_numbers": Game.drawResourceNumbers = !Game.drawResourceNumbers; break;
			case "dont_show_debris": Game.dontShowDebris = !Game.dontShowDebris; break;
			case "draw_current_time": Game.drawCurrentTime = !Game.drawCurrentTime; break;
			case "draw_inventory_bounds": Game.drawInventoryBounds = !Game.drawInventoryBounds; break;
			case "draw_center_lines": Game.drawCenterLines = !Game.drawCenterLines; break;
			case "dont_draw_player": Game.dontDrawPlayer = !Game.dontDrawPlayer; break;
			case "god": Game.godMode = !Game.godMode; break;
			case "god_mode": Game.godMode = !Game.godMode; break;
			case "no_clip": Game.noClip = !Game.noClip; break;
			case "noclip": Game.noClip = !Game.noClip; break;
			case "no_hunger": Game.noHunger = !Game.noHunger; break;
			case "draw_player_attack_bounds": Game.drawPlayerAttackBounds = !Game.drawPlayerAttackBounds; break;
			case "draw_right_click_bounds": Game.drawRightClickBounds = !Game.drawRightClickBounds; break;
			case "always_dawn": Game.alwaysDawn = !Game.alwaysDawn; break;
			case "always_day": Game.alwaysDay = !Game.alwaysDay; break;
			case "always_dusk": Game.alwaysDusk = !Game.alwaysDusk; break;
			case "always_night": Game.alwaysNight = !Game.alwaysNight; break;
			case "highlight_tiles": { Tile.drawHighLighted = !Tile.drawHighLighted; if(!Tile.drawHighLighted) Tile.resetAllHighlighted(); break; }
			
			case "reset": {
				Game.drawCollisionBounds=Game.noClip=Game.dontShowDebris=Game.drawCurrentTime=Game.drawInventoryBounds=Game.drawCenterLines=Game.dontDrawPlayer=Game.godMode=Game.noHunger=
				Game.drawResourceNumbers=Game.drawMobTerritoryZones=Game.drawMobAggroRadii=Game.alwaysDawn=Game.alwaysDay=Game.alwaysDusk=Game.alwaysNight=
				Game.drawPlayerAttackBounds=Game.drawRightClickBounds = false;
				Tile.drawHighLighted = false; Tile.resetAllHighlighted();
				Player.WALKING_SPEED = 1.75f; Player.RUNNING_SPEED = 3f; Game.clockSpeed = 1;
				break;
			}
			///////////////////////////////////////////////////////////
			
			case "give": {
					try{ // IF THIS SUCCEEDS, IT MUST BE USING ITEM ID
					     Integer.parseInt(item);
					  
					    	// IF THIS SUCCEEDS, THERE MUST BE A QUANTITY
					    	try { Integer.parseInt(value);
					    		  Inventory.addItem(Integer.parseInt(item), Integer.parseInt(value));
					    	} catch (NumberFormatException e) { 
					    		// ELSE THE QUANTITY MUST BE ONE
					    		Inventory.addItem(Integer.parseInt(item));
					    	}
					  
					} catch (NumberFormatException e) {
						// IF IT DIDN'T SUCCEED, IT MUST BE USING A STRING NAME FOR ITEM
						boolean isQuantity;
						
						// IF THIS SUCCEEDS, THERE MUST BE A QUANTITY
				    	try { Integer.parseInt(value); isQuantity = true;
				    	} catch (NumberFormatException ee) { isQuantity = false; }
						
						int ITEM_TO_ADD = -1;
						switch(item) {
							case "grass": ITEM_TO_ADD = PICKED_GRASS; break;
							case "flowers": ITEM_TO_ADD = PICKED_FLOWERS; break;
							case "crude_axe": ITEM_TO_ADD = CRUDE_AXE; break;
							case "logs": ITEM_TO_ADD = LOGS; break;
							case "crude_pickaxe": ITEM_TO_ADD = CRUDE_PICKAXE; break;
							case "rocks": ITEM_TO_ADD = ROCKS; break;
							case "sticks": ITEM_TO_ADD = STICKS; break;
							case "ashes": ITEM_TO_ADD = ASHES; break;
							case "torch": ITEM_TO_ADD = TORCH; break;
							case "rabbit_carcass": ITEM_TO_ADD = RABBIT_CARCASS; break;
							case "rabbit_foot": ITEM_TO_ADD = RABBIT_FOOT; break;
							case "rot": ITEM_TO_ADD = ROT; break;
							case "goblin_spear": ITEM_TO_ADD = GOBLIN_SPEAR; break;
							default: { System.out.println("invalid item identifier"); break OUTER_SWITCH; }
						}
						
						if(isQuantity) Inventory.addItem(ITEM_TO_ADD, Integer.parseInt(value));
						else  if(value.equalsIgnoreCase("max")) Inventory.addItem(ITEM_TO_ADD, Utils._2147483647);
							  else Inventory.addItem(ITEM_TO_ADD);
						
					}
					break;
			}
			
			case "spawn": {
				
				int quantity; 
		    	try { quantity = Integer.parseInt(value); // IF THIS SUCCEEDS, THERE MUST BE A QUANTITY
		    	} catch (NumberFormatException e) {  /* ELSE THE QUANTITY MUST BE ONE */ quantity = 1; }
				
		    	for(int i = 0; i < quantity; i++) {
		    		int offsetx = Utils.rand.nextInt(20) + 1;
		    		int offsety = Utils.rand.nextInt(20) + 1;
		    		if(Utils.rand.nextBoolean()) offsetx *= -1;
		    		if(Utils.rand.nextBoolean()) offsety *= -1;
		    		
;					switch(item) {
						case "rabbit":  new Rabbit(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "goblin":  new Goblin(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "boulder": new Boulder(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "sapling": new Sapling(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "grass":   new TallGrass(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "flower":  new Flower(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						case "tree":    new Tree(MouseInput.adjustedMouseXY.x+offsetx, MouseInput.adjustedMouseXY.y+offsety); break;
						default: System.out.println("invalid mob identifier");
					}
		    	}
				break;
			}
			
			case "create": {
				switch(item) {
					case "fire": Handler.structures.add(new Fire(new Point2f(MouseInput.adjustedMouseXY.x, MouseInput.adjustedMouseXY.y))); break;

					
					default: System.out.println("invalid structure identifier");
				}
				break;
			}
			
			case "teleport_to_biome": {
				outerSwitch:
				switch(item) {
					case "forest": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
								if(tile.biome == FOREST){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "volcanic": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
								if(tile.biome == VOLCANIC){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "desert": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == DESERT){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "taiga":{
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == TAIGA){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "tundra": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == SNOW_TUNDA){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "barren_rock":{
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == BARREN_ROCK){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "back_of_volcano": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == BACK_OF_VOLCANO){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "lake": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == LAKE){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "grass1": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == GRASS1){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					case "grass2": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == GRASS2){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					
					case "grass3": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == GRASS3){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
					
					case "grass4": {
						for(int i = 0; i < Handler.numSectionsRowsCols; i++) for(int j = 0; j < Handler.numSectionsRowsCols; j++) for(final Tile tile : Handler.world.get(i).get(j).get(0))
							if(tile.biome == GRASS4){ Player.pos.setLocation(tile.x, tile.y); break outerSwitch; }
					}
				} break;
			}
			
			case "exit": Game.cleanUp(); break;
			case "quit": Game.cleanUp(); break;
			
			
			// ############### SILLY STUFF BELOW #########################################
			case "HOLY_SHIT_BUNNIES": for(int i = 0; i < 1000; i++) {
		    		int offsetx = Utils.rand.nextInt(200) + 1; int offsety = Utils.rand.nextInt(200) + 1;
		    		if(Utils.rand.nextBoolean()) offsetx *= -1; if(Utils.rand.nextBoolean()) offsety *= -1;
				    new Rabbit(Player.center.x+offsetx, Player.center.y+offsety);
	    	} break;
	    	
			case "flower_bomb": {
				final Circle bounds = new Circle(Player.center, 300);
				for(int i = 0; i < 10000; i++) {
	    		int offsetx = Utils.rand.nextInt(300) + 1; int offsety = Utils.rand.nextInt(300) + 1;
	    		if(Utils.rand.nextBoolean()) offsetx *= -1; if(Utils.rand.nextBoolean()) offsety *= -1;
	    		if(bounds.contains(Player.center.x+offsetx, Player.center.y+offsety))
			    new Flower((int)(Player.center.x+offsetx), (int)(Player.center.y+offsety));
				} break; }
			// ############### END SILLY STUFF ###########################################
			
			default: System.out.println("invalid console command");
		}
		
		
		exit();
	}
	
	public static final void render(final Graphics2D g2d) {
		
		if(Game.state != STATE.DEVELOPER_CONSOLE) return;
		
		final int width = Game.game.getWidth();
		final int height = Game.game.getHeight();
		
		g2d.setColor(Color.black);
		g2d.fillRect(width/2 - width/4, height/2 - height/4, width/2, height/20);
		
		g2d.setColor(Color.white);
		g2d.setFont(fonts.fonts.ponderosa20);
		
		g2d.drawString(sb.toString(), width/2 - width/4 + 10, height/2 - height/4 + 20);
		
		g2d.setColor(pastelRed);
		g2d.setFont(fonts.fonts.Gregorian48Bold);
		g2d.drawString("console:", width/2 - width/4 + 2, height/2 - height/4 - 10);
		
		g2d.dispose();
	}
	
	private static synchronized final void open() {
		sb = new StringBuilder();
		tempState = Game.state;
		Game.state = STATE.DEVELOPER_CONSOLE;
	}
	
	private static synchronized final void exit() {
		index = history.size() - 1;
		sb = new StringBuilder();
		Game.state = tempState;
		tempState = null;
	}
}
