package graphics.scene;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.Timer;

import graphics.userInterface.Menu;
import graphics.userInterface.Window;
import system.battle.Coordinate;
import system.battle.Map;
import system.battle.Tile;
import system.character.Ally;
import system.character.BaseCharacter;
import system.item.skill.Skill;
import system.managers.Battle;
import system.managers.JogadaNetGames;
import system.managers.SceneManager;

public class BattleScene extends Scene {
	/**
	 * Serve pra não dar merda na serialização de objetos.
	 */
	private static final long serialVersionUID = -6373723891070836846L;
	
	private Battle battle;
	
	private Map map = new Map(16, 10);
	
	public Window attacker;
	public Window attackerParameters;
	public Window defender;
	public Window defenderParameters;
	public Window accuracyAndDamage;
	public Window info;
	public Window turnOrder;
	public Window mapCursor;
	public Window confirmation;
	public Window battleResults;
	
	private int posicao;
	private int mapCursorX;
	private int mapCursorY;
	
	private boolean cursorLock;
	private boolean battleStart;
	
	public Menu menu;
	public Menu battleCommands;
	
	private BufferedImage floor;
	private BufferedImage attack;
	private BufferedImage move;
	private BufferedImage radius;
	
	private BufferedImage heroSprite;
	private BufferedImage enemySprite;
	
	private String victoryString =  "\n\n\n\n\n\n\n\n\n\n\n\n"
			+ "                                                                                                                     "
			+ "Vitória\n"
			+ "                                                                                   "
			+ "Feche a janela para iniciar uma nova partida";
	
	private String defeatString =  "\n\n\n\n\n\n\n\n\n\n\n\n"
			+ "                                                                                                                     "
			+ "Derrota\n"
			+ "                                                                                   "
			+ "Feche a janela para iniciar uma nova partida";
	
	private SceneManager sceneManager;
	
	/**
	 * A cena de batalha
	 * @param width
	 * @param height
	 */
	public BattleScene(int width, int height, SceneManager sceneManager) {
		super(width, height);
		this.sceneManager = sceneManager;
		
		setLayout(null);
		setName("BattleScene");
		setBackground(Color.BLACK);
		
		battle = new Battle(map);

		attacker = createWindowWithText(0, 0, 80, 80, null);
		defender = createWindowWithText(688, 0, 80, 80, null);
		attackerParameters = createWindowWithText(80, 0, 180, 40, null);
		defenderParameters = createWindowWithText(510, 0, 180, 40, null);
		accuracyAndDamage = createWindowWithText(326, 40, 120, 40, "Precisão: 100%\nDano: 1234");
		info = createWindowWithText(144, 420, 480, 60, "Descrição da habilidade");
		turnOrder = createWindowWithText(0, 80, 80, 30, null);
		mapCursor = new Window(0, 0, 48, 48, false);
		confirmation = new Window(288, 200, 170, 26, "Confirma: K    /   Cancela: L");
		battleResults = new Window(0, 0, 768, 478, "");
		
		mapCursorX = 0;
		mapCursorY = 0;
		
		////////////////////////////////////////////////////////////////////////////////
		String[] opcoesMenu = new String[] {
			"Mover","Agir","Esperar"
		};
		menu = new Menu(150, 200, opcoesMenu);
		add(menu.getCursor());
		for (int i = 0; i < menu.getOptions().length; i++) {
			add(menu.getOptions()[i]);
		}
		
		createBattleCommands(battle.getAllies()[0]);
		battleCommands.hide();
		battleCommands.disable();

		menu.hide();
		menu.disable();
		
		Runnable cursorStandby = () -> cursorStandby();
		mapCursor.setExecuteMethod(cursorStandby);
		cursorLock = true;

		// Seta o commandMove como o gatilho inicial para o comando de movimento
		Runnable method = () -> commandMove();
		menu.getOptions()[0].setExecuteMethod(method);
		
		Runnable method2 = () -> commandAct();
		menu.getOptions()[1].setExecuteMethod(method2);
		
		Runnable method3 = () -> {
			try {
				commandWait();
			} catch (InterruptedException e) {e.printStackTrace();}};
		menu.getOptions()[2].setExecuteMethod(method3);
		
		Runnable cancelMethod = () -> commandStandby();
		menu.setCancelMethod(cancelMethod);
		//////////////////////////////////////////////////////////////////////////////
		add(info);
		add(attacker);
		add(attackerParameters);
		add(defender);
		add(defenderParameters);
		add(accuracyAndDamage);
		add(turnOrder);
		add(mapCursor);
		add(confirmation);
		add(battleResults);
		
		defender.hide();
		defenderParameters.hide();
		accuracyAndDamage.hide();
		info.hide();
		confirmation.hide();
		battleResults.hide();
		
		setComponentZOrder(battleResults, 0);
		
		loadTiles();
		
		drawMap();
		
		setSize(map.getTiles().length * 48, map.getTiles()[0].length * 48);
		setPreferredSize(new Dimension(map.getTiles().length * 48, map.getTiles()[0].length * 48));
	}

	/**
	 * Desenha o mapa na cena.
	 */
	public void drawMap() {
		for (int i = 0; i < map.getTiles().length; i++) {
			for (int j = 0; j < map.getTiles()[i].length; j++) {
				// Passa as imagens carregadas pros tiles
				Tile currentTile = map.getTiles()[i][j];
				
				currentTile.getSprite().setFloor(floor);
				currentTile.getSprite().setAttack(attack);
				currentTile.getSprite().setMove(move);
				currentTile.getSprite().setRadius(radius);
				currentTile.getSpriteUser().setHero(heroSprite);
				currentTile.getSpriteUser().setEnemySprite(enemySprite);
				
				if (currentTile.state == 0) {
					if (currentTile.getOccupant() != null) {
						currentTile.getSprite().changeTile("Floor", currentTile.getOccupant());
					} else {
						// Seta o tile como uma posição normal
						currentTile.getSprite().changeTile("Floor", null);
					}
				}
				
				if (currentTile.state == 1) {
					if (currentTile.getOccupant() != null) {
						currentTile.getSprite().changeTile("Move", currentTile.getOccupant());
					} else {
						// Deixa azul
						currentTile.getSprite().changeTile("Move", null);
					}
				}
				
				if (currentTile.state == 2) {
					if (currentTile.getOccupant() != null) {
						currentTile.getSprite().changeTile("Attack", currentTile.getOccupant());
					} else {
						// Deixa vermelho
						currentTile.getSprite().changeTile("Attack", null);
					}
				}
				
				if (currentTile.state == 3) {
					if (currentTile.getOccupant() != null) {
						currentTile.getSprite().changeTile("Radius", currentTile.getOccupant());
					} else {
						// Deixa amarelo
						currentTile.getSprite().changeTile("Radius", null);
					}
				}
				// Adiciona pra cena
				add(map.getTiles()[i][j].getSprite());
			}
		}
	}
	
	/**
	 * Desenha o mapa na cena.
	 */
	public void drawMap(ArrayList<Tile> tiles) {
		for (Tile tile : tiles) {
			tile.getSprite().setFloor(floor);
			tile.getSprite().setAttack(attack);
			tile.getSprite().setMove(move);
			tile.getSprite().setRadius(radius);
			tile.getSpriteUser().setHero(heroSprite);
			tile.getSpriteUser().setEnemySprite(enemySprite);
			
			if (tile.state == 0) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Floor", tile.getOccupant());
				} else {
					// Seta o tile como uma posição normal
					tile.getSprite().changeTile("Floor", null);
				}
			}
			
			if (tile.state == 1) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Move", tile.getOccupant());
				} else {
					// Deixa azul
					tile.getSprite().changeTile("Move", null);
				}
			}
			
			if (tile.state == 2) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Attack", tile.getOccupant());
				} else {
					// Deixa vermelho
					tile.getSprite().changeTile("Attack", null);
				}
			}
			
			if (tile.state == 3) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Radius", tile.getOccupant());
				} else {
					// Deixa amarelo
					tile.getSprite().changeTile("Radius", null);
				}
			}
		}
	}
	
	/**
	 * Desenha o mapa na cena.
	 */
	public void drawMap(ArrayList<Tile> tiles, Tile baseCharacterTile) {
		tiles.add(baseCharacterTile);
		
		for (Tile tile : tiles) {
			tile.getSprite().setFloor(floor);
			tile.getSprite().setAttack(attack);
			tile.getSprite().setMove(move);
			tile.getSprite().setRadius(radius);
			tile.getSpriteUser().setHero(heroSprite);
			tile.getSpriteUser().setEnemySprite(enemySprite);
			
			if (tile.state == 0) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Floor", tile.getOccupant());
				} else {
					// Seta o tile como uma posição normal
					tile.getSprite().changeTile("Floor", null);
				}
			}
			
			if (tile.state == 1) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Move", tile.getOccupant());
				} else {
					// Deixa azul
					tile.getSprite().changeTile("Move", null);
				}
			}
			
			if (tile.state == 2) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Attack", tile.getOccupant());
				} else {
					// Deixa vermelho
					tile.getSprite().changeTile("Attack", null);
				}
			}
			
			if (tile.state == 3) {
				if (tile.getOccupant() != null) {
					tile.getSprite().changeTile("Radius", tile.getOccupant());
				} else {
					// Deixa amarelo
					tile.getSprite().changeTile("Radius", null);
				}
			}
		}
	}
	
	public void createBattleCommands(Ally ally) {
		ArrayList<Skill> skills = ally.getSkillList();
		
		Skill[] allySkills = new Skill[skills.size()];
		String[] skillNames = new String[skills.size()];
		String[] skillDescriptions = new String[skills.size()];
		
		for (int i = 0; i < allySkills.length; i++) {
			allySkills[i] = skills.get(i);
			skillNames[i] = skills.get(i).getName();
			skillDescriptions[i] = skills.get(i).getDescription();
		}
		
		battleCommands = new Menu(menu.getX() + 250, menu.getY(), skillNames);
		add(battleCommands.getCursor());
		setComponentZOrder(battleCommands.getCursor(), 0);
		
		for (int i = 0; i < battleCommands.getOptions().length; i++) {
			add(battleCommands.getOptions()[i]);
			setComponentZOrder(battleCommands.getOptions()[i], 0);
		}
	}
	
	private void updateBattleCommands(Ally ally) {
		ArrayList<Skill> skills = ally.getSkillList();
		
		Skill[] allySkills = new Skill[skills.size()];
		String[] skillNames = new String[skills.size()];
		
		for (int i = 0; i < allySkills.length; i++) {
			allySkills[i] = skills.get(i);
			skillNames[i] = skills.get(i).getName();
		}
		
		for (int i = 0; i < battleCommands.getOptions().length; i++) {
			battleCommands.getOptions()[i].updateText(skillNames[i]);
			setComponentZOrder(battleCommands.getOptions()[i], 0);
		}
	}
	
	public void cursorMoveHorizontal(boolean direction) {
		System.out.println("CURSOR: "+mapCursorX+" "+mapCursorY);
		if (mapCursorX < map.getTiles().length - 1 && direction) {
			mapCursorX++;
			
			Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
			mapCursor.setLocation(location);
		}
		
		if (mapCursorX > 0 && !direction) {
			mapCursorX--;
			
			Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
			mapCursor.setLocation(location);
		}
	}
	
	public void cursorMoveVertical(boolean direction) {
		System.out.println("CURSOR: "+mapCursorX+" "+mapCursorY);
		if (mapCursorY < map.getTiles()[0].length - 1 && direction) {
			mapCursorY++;
			
			Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
			mapCursor.setLocation(location);
		}
		
		if (mapCursorY > 0 && !direction) {
			mapCursorY--;
			
			Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
			mapCursor.setLocation(location);
		}
	}
	
	public void commandStandby() {
		cursorLock = true;
	}

	/**
	 * Primeiro passo na ação de movimentação.
	 */
	public void commandMove() {
		menu.hide();
		
		ArrayList<Tile> moveRange = battle.getActiveUnit().ShowMovementRange(map.getTiles());
		showMovementRange(moveRange);
		drawMap(moveRange);
		
		ArrayList<Coordinate> moveRangeCoordinate = extractCoordinates(moveRange);

		cursorLock = false;
	
		Runnable methodConfirm = () -> commandMoveSelection(moveRange);
		Runnable methodCancel = () -> cancelCommandMove();	

		mapCursor.setExecuteMethod(methodConfirm);
		menu.setCancelMethod(methodCancel);
		
		BaseCharacter a = battle.getActiveUnit();
		
		mapCursorX = a.getCoordinate().getX();
		mapCursorY = a.getCoordinate().getY();
		
		Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
		mapCursor.setLocation(location);
		
		// TODO URGENTE ENVIAR PRO NETGAMES
		System.out.println("COMMAND MOVE");
		
		int tileState = 0;
		
		if (moveRange.size() != 0) {
			tileState = moveRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, moveRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	/**
	 * Cancelamento do comando de movimentação
	 */
	public void cancelCommandMove() {
		menu.show();
		
		ArrayList<Tile> moveRange = battle.getActiveUnit().ShowMovementRange(map.getTiles());
		hideRange(moveRange);
		drawMap(moveRange);
		
		ArrayList<Coordinate> moveRangeCoordinate = extractCoordinates(moveRange);
		
		cursorLock = true;
		
		confirmation.hide();
		
		// Cria uma função lambda que servirá como o novo método de execução do cursor em caso de confirmação
		Runnable method = () -> cursorStandby();
		
		// Atribui a porra do método pro caralho do cursor
		mapCursor.setExecuteMethod(method);
		
		System.out.println("Cancel Move");
		
		// TODO Envia para esconder
		int tileState = 0;
		
		if (moveRange.size() != 0) {
			tileState = moveRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, moveRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	/**
	 * Segundo passo na ação de movimentação. (CASO DE CONFIRMAÇÃO)
	 * @param moveRange - O alcance de movimentação do BaseCharacter
	 * @return O tile que o jogador quer se mover
	 */
	private void commandMoveSelection(ArrayList<Tile> moveRange) {
		// target é a posição a qual se deseja mover
		Tile target = battle.getMap().getTiles()[mapCursorX][mapCursorY];

		for (Tile tile : moveRange) {
			if (tile.equals(target)) {
				cursorLock = true;
				
				Runnable methodConfirm = () -> commandMoveConfirm(target, moveRange);

				mapCursor.setExecuteMethod(methodConfirm);
				confirmation.show();
//				menu.getOptions()[0].setExecuteMethod(methodConfirm);
				
//				Runnable method =  () -> commandMoveConfirm(target, moveRange);
//				confirmation.getOptions()[0].setExecuteMethod(method);
//				confirmation.getOptions()[1].setExecuteMethod(cancelMethod);
//				confirmation.setCancelMethod(cancelMethod);
				
//				Runnable methodCancel = () -> cancelCommandMoveSelection(moveRange);
				
//				menu.setCancelMethod(methodCancel);
//				Runnable methodCancel = () -> cancelCommandMoveSelection(moveRange);
				
				System.out.println("Command Move Selection");
				break;
			}
		}
		
		ArrayList<Coordinate> moveRangeCoordinate = extractCoordinates(moveRange);
		
		// TODO Envia para esconder
		int tileState = 0;
		
		if (moveRange.size() != 0) {
			tileState = moveRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, moveRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	/**
	 * Terceiro passo na ação de movimentação.
	 * @param target - Posição alvo para movimentação
	 * @param moveRange - Alcance da movimentação
	 */
	private void commandMoveConfirm(Tile target, ArrayList<Tile> moveRange) {
		confirmation.hide();
		hideRange(moveRange);
		
		Tile position = battle.getMap().getTiles()[battle.getActiveUnit().getCoordinate().getX()][battle.getActiveUnit().getCoordinate().getY()];
		
		battle.getActiveUnit().moveTo(map, target.getCoordinate());
		
		drawMap();
		
		Runnable methodCursor = () -> cursorStandby();
		mapCursor.setExecuteMethod(methodCursor);
		
		Runnable methodCancel = () -> commandStandby();
		menu.getOptions()[0].setExecuteMethod(methodCancel);		// Inutiliza a opção de se mover após ter se movido
		menu.setCancelMethod(methodCancel);
		menu.resetConfirmation();
		menu.show();
		
		// TODO O jogador tem que apertar L pra poder mover o cursor novamente
		// TODO URGENTE ENVIAR PRO NETGAMES
		System.out.println("COMMAND MOVE CONFIRM");
		
		// O moveTo não tá funcionando corretamente
		
		ArrayList<Coordinate> moveRangeCoordinate = extractCoordinates(moveRange);
		
		int tileState = 0;
		
		if (moveRange.size() != 0) {
			tileState = moveRange.get(0).state;
		}
		
//		for (Tile coordinate : moveRange) {
//			if (coordinate.getOccupant() != null) {
//				System.out.println(coordinate.getCoordinate().getX() +" "+ coordinate.getCoordinate().getY() + "    "+coordinate.getOccupant().getName());				
//			}
//		}
//		moveRange.add(battle.getMap().getTiles()[battle.getActiveUnit().getCoordinate().getX()][battle.getActiveUnit().getCoordinate().getY()]);
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, moveRangeCoordinate, position.getCoordinate(),
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	public void cursorStandby() {
		System.out.println("Cursor is on standby.");
	}

	public void commandAct() {
		BaseCharacter activeUnit = battle.getActiveUnit();
		
		if (activeUnit.getClass() == Ally.class) {
			for (int i = 0; i < battle.getAllies().length; i++) {
				if (battle.getAllies()[i].getName().equalsIgnoreCase(activeUnit.getName())) {
					System.out.println("É aliado então pode agir");
					updateBattleCommands(battle.getAllies()[i]);		// Fica esperto nessa linha
					Ally a = battle.getAllies()[i];
					
					// O cancelamento no menu só ocorre após a seleção de uma opção
					Runnable methodCancel = () -> cancelCommandAct(a);
					
					battleCommands.setCancelMethod(methodCancel);
					battleCommands.show();
					
					// Só pra dar um pequeno delay e não confirmar a primeira opção logo de cara
					Timer t = new Timer(100, new ActionListener() {
						@Override
						public void actionPerformed(ActionEvent e) {
							battleCommands.enable();							
						}
					});
					t.setRepeats(false);
					t.start();
					
					menu.disable();
					
					ArrayList<Skill> skills = activeUnit.getSkillList();
					
					for (int j = 0; j < battleCommands.getOptions().length; j++) {
						ArrayList<Tile> skillRange = skills.get(j).showSkillRange(map.getTiles());
						Skill aFuckingSkill = skills.get(j);
						
						Runnable methodOption = () -> commandActSelection(a, skillRange, aFuckingSkill);
						battleCommands.getOptions()[j].setExecuteMethod(methodOption);
					}
					
					mapCursorX = a.getCoordinate().getX();
					mapCursorY = a.getCoordinate().getY();
					
					Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
					mapCursor.setLocation(location);
					
					// --------------------------------------------------------------------

					sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
																	-1, -1, -1,
																	mapCursorX, mapCursorY, 0, null, null,
																	-1, -1, -1, -1, false, null, null, false, null, false, null,
																	false, battle.getActiveUnitIndex(), false));
					
					break;
				}
			}
		}
			
		System.out.println("COMMAND ACT");
	}
	
	private void cancelCommandAct(Ally ally) {
		System.out.println("Cancel Command ACT");
		
		menu.show();
		menu.enable();
		
		battleCommands.hide();
		battleCommands.disable();
		
		confirmation.hide();
		
		cursorLock = true;
		
		Runnable method = () -> cursorStandby();
		mapCursor.setExecuteMethod(method);
		
		mapCursorX = ally.getCoordinate().getX();
		mapCursorY = ally.getCoordinate().getY();
		
		Point location = map.getTiles()[mapCursorX][mapCursorY].getSprite().getLocation();
		mapCursor.setLocation(location);
		
		// --------------------------------------------------------------------

		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, 0, null, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	private void commandActSelection(Ally ally, ArrayList<Tile> skillRange, Skill skill) {
		menu.hide();
		battleCommands.hide();
		
		showAttackRange(skillRange);
		
		Runnable method = () -> commandActSelection(ally, skillRange, skill);
		mapCursor.setExecuteMethod(method);
		
		Runnable cancelMethod = () -> cancelCommandActSelection(skillRange);
		battleCommands.setCancelMethod(cancelMethod);
		
		cursorLock = false;
		
		// target é a posição a qual se deseja mover
		Tile target = battle.getMap().getTiles()[mapCursorX][mapCursorY];
		
		String defenderText = "";
		String defenderParamText = "";

		for (Tile tile : skillRange) {
			if (tile.getOccupant() != null && tile.equals(target)) {
				BaseCharacter occupant = tile.getOccupant();
				
				System.out.println(occupant.getName());
				cursorLock = true;
				
				defender.updateText(tile.getOccupant().getName());
				defenderText = tile.getOccupant().getName();
				defender.show();
				
				defenderParameters.updateText(	"HP: " + occupant.getTotalHp() + " / " + occupant.getTotalHpMax() +
												"\nMP: " + occupant.getTotalMp() + " / " + occupant.getTotalMpMax());
				defenderParamText = "HP: " + occupant.getTotalHp() + " / " + occupant.getTotalHpMax() +
									"\nMP: " + occupant.getTotalMp() + " / " + occupant.getTotalMpMax();
				defenderParameters.show();
				
				final String aux1 = defenderText;
				final String aux2 = defenderParamText;

				Runnable methodConfirm = () -> commandActShowRadius(target, skillRange, skill, aux1, aux2);
				mapCursor.setExecuteMethod(methodConfirm);
				
				System.out.println("Command Act Selection");
				
				break;
			}
		}
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> skillRangeCoordinate = extractCoordinates(skillRange);
		
		int tileState = 0;
		
		if (skillRange.size() != 0) {
			tileState = skillRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, skillRangeCoordinate, null,
														-1, -1, -1, -1,
														true, defenderText, defenderParamText,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	private void cancelCommandActSelection(ArrayList<Tile> skillRange) {
		System.out.println("Cancel Command Act Selection");
		
		hideRange(skillRange);
		
		menu.show();
		menu.enable();
		
		battleCommands.hide();
		battleCommands.disable();
		
		confirmation.hide();
		
		cursorLock = true;
		
		defender.hide();
		defenderParameters.hide();
		
		Runnable method = () -> cursorStandby();
		mapCursor.setExecuteMethod(method);
		
		// TODO--------------------------------------------------------------------
		
		ArrayList<Coordinate> moveRangeCoordinate = extractCoordinates(skillRange);
		
		int tileState = 0;
		
		if (skillRange.size() != 0) {
			tileState = skillRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, moveRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	private void commandActShowRadius(Tile target, ArrayList<Tile> skillRange, Skill skill, String defenderText, String defenderParamText) {
		System.out.println("Command Act Show Radius");
		
		ArrayList<Tile> radiusRange = skill.showRadius(map.getTiles(), target.getCoordinate());
		
		showRadiusRange(radiusRange);
		
		Runnable methodCancel = () -> cancelCommandActShowRadius(target, skillRange, radiusRange);
		battleCommands.setCancelMethod(methodCancel);
		
		int lower = (skill.getStaticValue() * (100 - skill.getVariability())) / 100;
		int higher = (skill.getStaticValue() * (100 + skill.getVariability())) / 100;
		int numberOfHits = skill.getNumberOfHits();
		
		String accuracy = "Precisão: " + skill.getSkillAccuracy() + "%";
		String damage = "Dano: " + lower + "~" + higher + "    (x" + numberOfHits + ")";
		
		accuracyAndDamage.updateText(accuracy + "\n" + damage);
		accuracyAndDamage.show();
		
		// Os alvos do raio
		ArrayList<BaseCharacter> targets = new ArrayList<BaseCharacter>();
		
		// Adeda os que não forem nulos
		for (Tile tile : radiusRange) {
			if (tile.getOccupant() != null) {
				targets.add(tile.getOccupant());
			}
		}
		
		BaseCharacter[] targets2 = new BaseCharacter[targets.size()];
		
		for (int i = 0; i < targets.size(); i++) {
			targets2[i] = targets.get(i);
		}
		
		Runnable methodConfirm = () -> commandActConfirm(	targets2, skillRange, radiusRange, skill,
															defenderText, defenderParamText, accuracy, damage);
		mapCursor.setExecuteMethod(methodConfirm);
		
		// TODO O jogador tem que apertar L pra poder mover o cursor novamente
		// TODO URGENTE ENVIAR PRO NETGAMES
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> radiusRangeCoordinate = extractCoordinates(radiusRange);
		
		int tileState = 0;
		
		if (radiusRange.size() != 0) {
			tileState = radiusRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileState, radiusRangeCoordinate, null,
														-1, -1, -1, -1, 
														true, defenderText, defenderParamText,
														true, accuracy + "\n" + damage,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}

	private void cancelCommandActShowRadius(Tile target, ArrayList<Tile> skillRange, ArrayList<Tile> radiusRange) {
		System.out.println("Cancel Command Act Show Radius");
		
		hideRange(radiusRange);
		hideRange(skillRange);
		
		menu.show();
		menu.enable();
		
		battleCommands.hide();
		battleCommands.disable();
		
		confirmation.hide();
		
		accuracyAndDamage.hide();
		
		cursorLock = true;
		
		defender.hide();
		defenderParameters.hide();
		
		Runnable method = () -> cursorStandby();
		mapCursor.setExecuteMethod(method);
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> skillRangeCoordinate = extractCoordinates(skillRange);
		ArrayList<Coordinate> radiusRangeCoordinate = extractCoordinates(radiusRange);
		
		int tileStateSkill = 0;
		int tileStateRadius = 0;
		
		if (skillRange.size() != 0) {
			tileStateSkill = skillRange.get(0).state;
		}
		
		if (radiusRange.size() != 0) {
			tileStateRadius = skillRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateSkill, skillRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateRadius, radiusRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}

	private void commandActConfirm(	BaseCharacter[] targets, ArrayList<Tile> skillRange, ArrayList<Tile> radiusRange, Skill skill,
									String defenderText, String defenderParamText, String accuracy, String damage) {
		System.out.println("Command Act Confirm");
		
		confirmation.show();
		
		Runnable methodConfirm = () -> commandActExecuteHits(targets, skillRange, radiusRange, skill);
		mapCursor.setExecuteMethod(methodConfirm);
		
		Runnable methodCancel = () -> cancelCommandActExecuteHits(skillRange, radiusRange);
		battleCommands.setCancelMethod(methodCancel);
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> skillRangeCoordinate = extractCoordinates(skillRange);
		ArrayList<Coordinate> radiusRangeCoordinate = extractCoordinates(radiusRange);
		
		int tileStateSkill = 0;
		int tileStateRadius = 0;
		
		if (skillRange.size() != 0) {
			tileStateSkill = skillRange.get(0).state;
		}
		
		if (radiusRange.size() != 0) {
			tileStateRadius = radiusRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateSkill, skillRangeCoordinate, null,
														-1, -1, -1, -1, 
														true, defenderText, defenderParamText,
														true, accuracy + "\n" + damage,
														false, null,
														false, battle.getActiveUnitIndex(), false));
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateRadius, radiusRangeCoordinate, null,
														-1, -1, -1, -1, 
														true, defenderText, defenderParamText,
														true, accuracy + "\n" + damage,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	public void commandActExecuteHits(BaseCharacter[] targets, ArrayList<Tile> skillRange, ArrayList<Tile> radiusRange, Skill skill) {
		System.out.println("Command Act Execute Hits");
		// [alvo][dano e hits]
		int[][] skillExecutionResults = skill.executeSkill(targets);

		// Atualiza a caixa de texto do primeiro alvo apenas
		defenderParameters.updateText(	"HP: " + targets[0].getTotalHp() + " / " + targets[0].getTotalHpMax() +
										"\nMP: " + targets[0].getTotalMp() + " / " + targets[0].getTotalMpMax());
		
		accuracyAndDamage.hide();
		
		String notification = "";
		
		for (int i = 0; i < skillExecutionResults.length; i++) {
			notification += targets[i].getName() + " recebeu " + skillExecutionResults[i][0] + " de dano e " + skillExecutionResults[i][1] + " golpes.\n";
			
			if (targets[i].getTotalHp() == 0) {
				targets[i].die();
				radiusRange.get(i).setOccupant(null);
				
				drawMap();

				System.out.println("MORREU " + targets[i].getName());
			}
		}
		
		// info tem apenas 3 linhas pra escrever 66 caracteres
		info.updateText(notification);
		info.show();
		// Pra esconder a info após 5 segundos
		Timer infoTimer = new Timer(5000, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				info.hide();
			}
		});
		infoTimer.setRepeats(false);
		infoTimer.start();

		menu.resetConfirmation();
		
		confirmation.hide();
		
		cancelCommandActExecuteHits(skillRange, radiusRange);
		
		Runnable methodCursor = () -> cursorStandby();
		mapCursor.setExecuteMethod(methodCursor);
		
		// TODO DESCOMENTAR AS LINHAS ABAIXO
		Runnable method = () -> commandStandby();
		menu.getOptions()[1].setExecuteMethod(method);
		
		if (battle.victory()) {
			System.out.println("-----------------------VITORY-----------------------");
			battleResults.updateText(victoryString);
			battleResults.show();
			
			Runnable methodStandTheFuckBy = () -> commandStandby();
			battleCommands.getOptions()[0].setExecuteMethod(methodStandTheFuckBy);
			battleCommands.getOptions()[1].setExecuteMethod(methodStandTheFuckBy);
			battleCommands.getOptions()[2].setExecuteMethod(methodStandTheFuckBy);
		}
		
		if (battle.defeat()) {
			System.out.println("-----------------------DEFEAT-----------------------");
			battleResults.updateText(defeatString);
			battleResults.show();
			
			Runnable methodStandTheFuckBy = () -> commandStandby();
			battleCommands.getOptions()[0].setExecuteMethod(methodStandTheFuckBy);
			battleCommands.getOptions()[1].setExecuteMethod(methodStandTheFuckBy);
			battleCommands.getOptions()[2].setExecuteMethod(methodStandTheFuckBy);
		}
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> skillRangeCoordinate = extractCoordinates(skillRange);
		ArrayList<Coordinate> radiusRangeCoordinate = extractCoordinates(radiusRange);
		
		int tileStateSkill = 0;
		int tileStateRadius = 0;
		
		if (skillRange.size() != 0) {
			tileStateSkill = skillRange.get(0).state;
		}
		
		if (radiusRange.size() != 0) {
			tileStateRadius = skillRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateSkill, skillRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														true, notification,
														false, battle.getActiveUnitIndex(), false));
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateRadius, radiusRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														true, notification,
														false, battle.getActiveUnitIndex(), false));
	}
	
	private void cancelCommandActExecuteHits(ArrayList<Tile> skillRange, ArrayList<Tile> radiusRange) {
		System.out.println("Cancel Command Act Execute Hits");
		
		hideRange(radiusRange);
		hideRange(skillRange);
		
		menu.show();
		menu.enable();
		
		battleCommands.hide();
		battleCommands.disable();
		
		confirmation.hide();
		
		accuracyAndDamage.hide();
		
		cursorLock = true;
		
		defender.hide();
		defenderParameters.hide();
		
		Runnable method = () -> cursorStandby();
		mapCursor.setExecuteMethod(method);
		
		// --------------------------------------------------------------------
		
		ArrayList<Coordinate> skillRangeCoordinate = extractCoordinates(skillRange);
		ArrayList<Coordinate> radiusRangeCoordinate = extractCoordinates(radiusRange);
		
		int tileStateSkill = 0;
		int tileStateRadius = 0;
		
		if (skillRange.size() != 0) {
			tileStateSkill = skillRange.get(0).state;
		}
		
		if (radiusRange.size() != 0) {
			tileStateRadius = skillRange.get(0).state;
		}
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateSkill, skillRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
		
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, tileStateRadius, radiusRangeCoordinate, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														false, battle.getActiveUnitIndex(), false));
	}
	
	public void commandWait() throws InterruptedException {
		menu.hide();
		menu.reset();
		menu.resetConfirmation();
		menu.disable();
		
		confirmation.hide();
		
		cursorLock = true;
		
		// Reseta a porra dos comandos
		Runnable method = () -> commandMove();
		menu.getOptions()[0].setExecuteMethod(method);
		
		Runnable method2 = () -> commandAct();
		menu.getOptions()[1].setExecuteMethod(method2);
		
//		passTurnBattleScene();
		
		minhaVez = false;
		
		// O Wait não tá passando a vez
		sceneManager.enviarJogada(new JogadaNetGames(	battle.getRoundOrder(), battle.getRoundOrder(), false,
														-1, -1, -1,
														mapCursorX, mapCursorY, 0, null, null,
														-1, -1, -1, -1, 
														false, null, null,
														false, null,
														false, null,
														true, battle.getActiveUnitIndex(), true));
	}
	
	// Passa vez
	public void passTurnBattleScene() throws InterruptedException {
		// Aqui passou o turno
		battle.passTurn();
		
		// Não dá de computar o próximo no turno do atual
		Random random = new Random();
		int chosenSkill = random.nextInt(battle.getEnemies()[0].getSkillList().size());					// TODO AQUI
		int chosenFoeIndex = random.nextInt(battle.getAllies().length);		// TODO AQUI
		
		// Inicia o turno da unidade ativa
		// Fora o parâmetro battleScene os demais não interferem em nada nos aliados
		battle.getActiveUnit().turn(this, chosenSkill, chosenFoeIndex, 0, true);		// TODO ALTERAR AQUI -1 -1 -1 false
	}
	
	private boolean minhaVez;
	
	/**
	 * Inicia o turno de um dos jogadores
	 */
	public void allyTurnStart(int asd) {
		// Gera a porra de um deadlock na execução do programa:
		// O jogador que devia receber não faz nada e o que enviou espera eternamente
		if (posicao == asd) {
			menu.show();
			menu.enable();		
		}
	}
	
	/**
	 * Se der certo faz o menu executar o que executaria em um estado inicial
	 */
	public void resetMenu() {
		Runnable cursorStandby = () -> cursorStandby();
		mapCursor.setExecuteMethod(cursorStandby);
		cursorLock = true;

		// Seta o commandMove como o gatilho inicial para o comando de movimento
		Runnable method = () -> commandMove();
		menu.getOptions()[0].setExecuteMethod(method);
		
		Runnable method2 = () -> commandAct();
		menu.getOptions()[1].setExecuteMethod(method2);
		
		Runnable method3 = () -> {
			try {
				commandWait();
			} catch (InterruptedException e) {e.printStackTrace();}};
		menu.getOptions()[2].setExecuteMethod(method3);
		
		Runnable cancelMethod = () -> commandStandby();
		menu.setCancelMethod(cancelMethod);
	}
	
	/**
	 * Usar no netgames
	 * @throws InterruptedException
	 */
	public void battleStart(BaseCharacter[] roundOrder, int asd, int asdd, int asddd, boolean qwe) throws InterruptedException {
		battle.setRoundOrder(roundOrder);
		battle.getActiveUnit().turn(this, asd, asdd, asddd, qwe);		// TODO ALTERAR AQUI
	}
	
	/**
	 * Move a interface gráfica pra melhor visualização do mapa
	 */
	private void relocateUi() {
		if (mapCursorY <= 2) {
			attacker.setLocation(0, 370);
			attackerParameters.setLocation(80, 370);
			turnOrder.setLocation(0, 450);
			defender.setLocation(688, 370);
			defenderParameters.setLocation(510, 370);
		} else {
			attacker.setLocation(0, 0);
			attackerParameters.setLocation(80, 0);
			turnOrder.setLocation(0, 80);
			defender.setLocation(688, 0);
			defenderParameters.setLocation(510, 0);
		}
		
		if ((mapCursorX >= 3 && mapCursorX <= 12) && (mapCursorY >= 8 && mapCursorY <= 9)) {
			info.setLocation(144, 320);
		} else {
			info.setLocation(144, 420);
		}
	}
	
	public void inputManager(KeyEvent e) throws InterruptedException {
		mapCursor.inputManager(e);
		menu.inputManager(e);
		battleCommands.inputManager(e);
		
		/*
		 * Tá dando erro pelo seguinte:
		 * Quando chama o input manager no confirmation, especialmente a parte da confirmação
		 * não tem porra nenhuma para ser executada por padrão, logo uma exceção é jogada.
		 * 
		 * Assim, vai ter que ter alguma palhaçada inicializada antes do inputManager ser dado ao confirmation
		 */
				
		// Permite mover o cursor livremente pelo mapa 
		if (!cursorLock) {
			// Cima
			if (e.getKeyCode() == KeyEvent.VK_W) {
				cursorMoveVertical(false);
				relocateUi();
			}
			
			// Baixo
			if (e.getKeyCode() == KeyEvent.VK_S) {
				cursorMoveVertical(true);
				relocateUi();
			}
			
			// Esquerda
			if (e.getKeyCode() == KeyEvent.VK_A) {
				cursorMoveHorizontal(false);
				relocateUi();
			}
			
			// Direita
			if (e.getKeyCode() == KeyEvent.VK_D) {
				cursorMoveHorizontal(true);
				relocateUi();
			}			
		}
	}
	
	/**
	 * Deixa o tile azulado pra indicar que é válido para a movimentação
	 * @param range a lista de tiles a serem alterados
	 */
	public void showMovementRange(ArrayList<Tile> range) {
		for (Tile tile : range) {
			tile.state = 1;

			if (tile.getOccupant() != null) {
				tile.getSprite().changeTile("Movement", tile.getOccupant());
			} else {
				tile.getSprite().changeTile("Movement", null);				
			}
		}
	}
	
	/**
	 * Deixa o tile avermelhado pra indicar que é válido para ataques
	 * @param range a lista de tiles a serem alterados
	 */
	public void showAttackRange(ArrayList<Tile> range) {
		for (Tile tile : range) {
			tile.state = 2;
			if (tile.getOccupant() != null) {
				tile.getSprite().changeTile("Attack", tile.getOccupant());
			} else {
				tile.getSprite().changeTile("Attack", null);
			}
		}
	}
	
	/**
	 * Deixa o tile amarelado pra indicar que são a área de ação de um ataque
	 * @param range a lista de tiles a serem alterados
	 */
	public void showRadiusRange(ArrayList<Tile> range) {
		for (Tile tile : range) {
			tile.state = 3;

			if (tile.getOccupant() != null) {
				tile.getSprite().changeTile("Radius", tile.getOccupant());
			} else {
				tile.getSprite().changeTile("Radius", null);				
			}
		}
	}
	
	/**
	 * Envia as coordenadas dos tiles pelo netgames pra depois decodificá-las via convertCoordinates
	 * @param moveRange
	 * @return
	 */
	public ArrayList<Coordinate> extractCoordinates(ArrayList<Tile> moveRange) {
		ArrayList<Coordinate> extracted = new ArrayList<Coordinate>();
		
		for (Tile tile : moveRange) {
			extracted.add(tile.getCoordinate());
//			System.out.println("Extracted: " + tile.getCoordinate().getX() + "," + tile.getCoordinate().getY());
		}
		
		return extracted;
	}
	
	/**
	 * Se der tudo certo converte as coordenadas em tiles
	 * @param coordinates
	 * @return
	 */
	public ArrayList<Tile> convertCoordinates(ArrayList<Coordinate> coordinates) {
		ArrayList<Tile> converted = new ArrayList<Tile>();
		ArrayList<Tile> tilesArrayList = new ArrayList<Tile>();
		
		Tile[][] tiles = battle.getMap().getTiles();
		
		// Converte o mapa inteiro para ArrayList
		for (int i = 0; i < tiles.length; i++) {
			for (int j = 0; j < tiles[i].length; j++) {
				tilesArrayList.add(tiles[i][j]);
			}
		}
		
		// Eu tenho todas as coordenadas e eu quero seus tiles
		// Como fazer para comparar todos as coordenadas com os pontos?
		
		// TODO Pode dar merda aqui.
		for (int i = 0; i < tilesArrayList.size(); i++) {
			for (int j = 0; j < coordinates.size(); j++) {
				// Se as coordenadas forem iguais quebra este loop e adeda o tile para converted
				if (coordinates.get(j).getX() == tilesArrayList.get(i).getCoordinate().getX() &&
					coordinates.get(j).getY() == tilesArrayList.get(i).getCoordinate().getY()) {
					converted.add(tilesArrayList.get(i));
					break;
				}
			}
		}
		
		
//		for (int i = 0; i < converted.size(); i++) {
//			System.out.println("CONVERTED: " + converted.get(i).getCoordinate().getX() + "," + converted.get(i).getCoordinate().getY());
//		}
		
		return converted;
	}
	
	/**
	 * Reverte a imagem do tile para a imagem padrão do chão
	 * @param range a lista de tiles a serem alterados
	 */
	public void hideRange(ArrayList<Tile> range) {
		for (Tile tile : range) {
			tile.state = 0;

			if (tile.getOccupant() != null) {
				tile.getSprite().changeTile("Floor", tile.getOccupant());
			} else {
				tile.getSprite().changeTile("Floor", null);				
			}
		}
	}
	
	/**
	 * Carrega tudo que é necessário para todos os tiles uma só vez. Isso é pra otimizar a bagaça
	 */
	private void loadTiles() {
		String path = "resources/images/tiles/Tiles";
		String extension = ".png";
		// Carrega um tile verdinho por padrão
		floor = loadSprite(path + extension);
		attack = loadSprite(path + extension);
		move = loadSprite(path + extension);
		radius = loadSprite(path + extension);
		
		floor = floor.getSubimage(4 * 96, 6 * 96, 96, 96);
		attack = attack.getSubimage(5 * 96, 1 * 96, 96, 96);
		move = move.getSubimage(3 * 96, 1 * 96, 96, 96);
		radius = radius.getSubimage(4 * 96, 1 * 96, 96, 96);
		
		Random random = new Random();
		String[] heroes = new String[]{"resources/images/tiles/Actor1.png"};
		String[] monsters = new String[]{"resources/images/tiles/Monster1.png"};
		
		heroSprite = loadSprite(heroes[random.nextInt(heroes.length)]);
		enemySprite = loadSprite(monsters[random.nextInt(monsters.length)]);

		// Diminui o tamanho pela metade
		double scale = 0.5;
		
		BufferedImage aux = new BufferedImage(floor.getWidth()/2, floor.getHeight()/2, BufferedImage.TYPE_INT_ARGB);
		aux = rescale(floor, aux, scale, scale);
		
		BufferedImage auxAttack = new BufferedImage(attack.getWidth()/2, attack.getHeight()/2, BufferedImage.TYPE_INT_ARGB);
		auxAttack = rescale(attack, auxAttack, scale, scale);
		
		BufferedImage auxMove = new BufferedImage(move.getWidth()/2, move.getHeight()/2, BufferedImage.TYPE_INT_ARGB);
		auxMove = rescale(move, auxMove, scale, scale);
		
		BufferedImage auxRadius = new BufferedImage(radius.getWidth()/2, radius.getHeight()/2, BufferedImage.TYPE_INT_ARGB);
		auxRadius = rescale(radius, auxRadius, scale, scale);
		
		// Seta a imagem como a que foi reescalada
		floor = aux;
		attack = auxAttack;
		move = auxMove;
		radius = auxRadius;
	}
	
	/**
	 * Carrega um sprite do arquivo file
	 * @param file o nome do arquivo
	 * @return uma BufferedImage contendo a imagem que se deseja
	 */
	private BufferedImage loadSprite(String file) {

		BufferedImage sprite = null;

		try {
        	sprite = ImageIO.read(new File( file ));
        } catch (IOException e) {
        	e.printStackTrace();
        }

        return sprite;
	}

 	/**
 	 * Reescala uma imagem com o AffineTransform pelas proporções horizontalRatio e verticalRatio
 	 * @param source a imagem que será reescalada
 	 * @param destination a imagem que servirá como recipiente da gerada (DICA: ESSA PORRA NÃO PODE SER IGUAL A source DE JEITO MANEIRA)
 	 * @param horizontalRatio a proporção horizontal da escala
 	 * @param verticalRatio a proporção vertical da escala
 	 * @return source escalada conforme as ratios
 	 */
    private BufferedImage rescale(BufferedImage source, BufferedImage destination, double horizontalRatio, double verticalRatio) {
 		AffineTransform affine = new AffineTransform();
 		affine.scale(horizontalRatio, verticalRatio);
 		
 		AffineTransformOp scale = new AffineTransformOp(affine, AffineTransformOp.TYPE_BILINEAR);
 		return scale.filter(source, destination);
 	}

	public Battle getBattle() {
		return battle;
	}

	
	public boolean isBattleStart() {
		return battleStart;
	}
	

	public void setBattleStart(boolean battleStart) {
		this.battleStart = battleStart;
	}

	public int getPosicao() {
		return posicao;
	}

	public void setPosicao(int posicao) {
		this.posicao = posicao;
	}

	public int getMapCursorX() {
		return mapCursorX;
	}

	public void setMapCursorX(int mapCursorX) {
		this.mapCursorX = mapCursorX;
	}

	public int getMapCursorY() {
		return mapCursorY;
	}

	public void setMapCursorY(int mapCursorY) {
		this.mapCursorY = mapCursorY;
	}

	public Map getMap() {
		return map;
	}

	public boolean isMinhaVez() {
		return minhaVez;
	}

	public void setMinhaVez(boolean minhaVez) {
		this.minhaVez = minhaVez;
	}

	public SceneManager getSceneManager() {
		return sceneManager;
	}
}
