package system.managers;

import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.Timer;

import br.ufsc.inf.leobr.cliente.Jogada;
import br.ufsc.inf.leobr.cliente.OuvidorProxy;
import br.ufsc.inf.leobr.cliente.Proxy;
import br.ufsc.inf.leobr.cliente.exception.ArquivoMultiplayerException;
import br.ufsc.inf.leobr.cliente.exception.JahConectadoException;
import br.ufsc.inf.leobr.cliente.exception.NaoConectadoException;
import br.ufsc.inf.leobr.cliente.exception.NaoJogandoException;
import br.ufsc.inf.leobr.cliente.exception.NaoPossivelConectarException;
import graphics.scene.BattleScene;
import graphics.scene.ConnectionScene;
import graphics.scene.Scene;
import system.battle.Coordinate;
import system.battle.Tile;
import system.character.BaseCharacter;

public class SceneManager extends JFrame implements OuvidorProxy {
	/**
	 * Serve pra não dar merda na serialização de objetos.
	 */
	private static final long serialVersionUID = 325128910572784955L;

	private Scene currentScene;
	private ArrayList<Scene> sceneQueue = new ArrayList<Scene>();
	
	public static final int WIDTH = 774;
	public static final int HEIGHT = 508;
	public static final int SCALE = 1;

	private ConnectionScene connectionScene;
	private BattleScene battleScene;
	
	private Proxy proxy;
		
	private KeyListener input = new KeyListener() {
		@Override
		public void keyTyped(KeyEvent e) {		}
		
		@Override
		public void keyReleased(KeyEvent e) {		}
		
		@Override
		public void keyPressed(KeyEvent e) {
			try {inputManager(e);}
			catch (InterruptedException e1) {e1.printStackTrace();}
		}
	};
	
	private boolean connectionSceneAble = true;
	
	private int posicao;
	
	public SceneManager() {
		proxy = Proxy.getInstance();
		proxy.addOuvinte(this);
		
		connectionScene = new ConnectionScene(WIDTH * SCALE, HEIGHT * SCALE, this);
		battleScene = new BattleScene(WIDTH * SCALE, HEIGHT * SCALE, this);					// Só pra não dar muita merda
		
		sceneQueue.add(battleScene);
		sceneQueue.add(connectionScene);
		
		currentScene = connectionScene;
		setContentPane(currentScene);
		
		setTitle("Monster Hunter Tactics");
		setBackground(Color.BLACK);
		addKeyListener(input);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setResizable(false);
		setSize(WIDTH * SCALE, HEIGHT * SCALE);
		setLocationRelativeTo(null);
		setVisible(true); 
		setFocusable(true);
		setLayout(null);
		requestFocus();
	}
	
	/**
	 * Vai para a cena de nome name
	 * @param name nome da cena.<br>Valores válidos: BattleScene, ModeSelectionScene
	 */
	public void goToScene(String name) {
		for (Scene scene : sceneQueue) {
			if (scene.getName().equalsIgnoreCase(name)) {
				currentScene = scene;
				setContentPane(scene);
				getContentPane().addKeyListener(input);
				break;
			}
		}
	}
	
	/**
	 * Vai para a cena de índice index
	 * @param index o índice da cena na pilha de cenas.
	 */
	public void goToScene(int index) {
		currentScene = sceneQueue.get(index);
		setContentPane(sceneQueue.get(index));
		getContentPane().addKeyListener(input);
	}
		
	/**
	 * Use pra receber a entrada de dados em outras cenas
	 * @param e o key event
	 * @throws InterruptedException 
	 */
	private void inputManager(KeyEvent e) throws InterruptedException {
		if (connectionSceneAble) {
			connectionScene.inputManager(e);			
		}
		battleScene.inputManager(e);
	}

	public void iniciarPartidaRedeJogador1() {
		// Só dá pra enviar tipos primitivos e serializaveis

		BaseCharacter[] participantes = new BaseCharacter[] {
				battleScene.getBattle().getAllies()[0], battleScene.getBattle().getAllies()[1],
				
				battleScene.getBattle().getEnemies()[0], battleScene.getBattle().getEnemies()[1],
				battleScene.getBattle().getEnemies()[2], battleScene.getBattle().getEnemies()[3],
				battleScene.getBattle().getEnemies()[4]
		};
		
		BaseCharacter[] roundOrder = battleScene.getBattle().getRoundOrder();
		
		connectionSceneAble = false;
		
		// Começa "passando" a vez
		enviarJogada(new JogadaNetGames(roundOrder, participantes, connectionSceneAble, -1, -1, -1, 0, 0, 0, null, null,
										-1, -1, -1, -1, 
										false, null, null,
										false, null,
										false, null,
										false, battleScene.getBattle().getActiveUnitIndex(), false));// TODO Cara pode dar merda aqui
		
		receberJogada(new JogadaNetGames(roundOrder, participantes, connectionSceneAble, -1, -1, -1, 0, 0, 0, null, null,
										-1, -1, -1, -1, 
										false, null, null,
										false, null,
										false, null,
										true, battleScene.getBattle().getActiveUnitIndex(), false));
		
		goToScene("BattleScene");
		
		Random random = new Random();
		int chosenSkill = random.nextInt(battleScene.getBattle().getEnemies()[0].getSkills().length);					// TODO AQUI
		int chosenFoeIndex = random.nextInt(battleScene.getBattle().getAllies().length);		// TODO AQUI
		
		// TODO Aqui inicia para o player1
		try {battleScene.battleStart(battleScene.getBattle().getRoundOrder(), chosenSkill, chosenFoeIndex, 0, false);}// FICA ESPERTO AQUI
		catch (InterruptedException e) {e.printStackTrace();}
	}
	
	public void setBattleScene(BattleScene battleScene) {
		this.battleScene = battleScene;
	}

	public BattleScene getBattleScene() {
		return battleScene;
	}
	
	// ------------------------------------------------------------------------
	// Lixo do netgames daqui pra baixo
	// ------------------------------------------------------------------------
	public void conectar(String servidor, String nome) {
		try {
			proxy.conectar(servidor, nome);
		} catch (JahConectadoException e) {
			JOptionPane.showMessageDialog(this, "Já está conectado");
			e.printStackTrace();
		} catch (NaoPossivelConectarException e) {
			JOptionPane.showMessageDialog(this, e.getMessage());
			e.printStackTrace();
		} catch (ArquivoMultiplayerException e) {
			JOptionPane.showMessageDialog(this, e.getMessage());
			e.printStackTrace();
		}
	}
	
	
	public boolean iniciarPartidaRede() {
		boolean successful = false;
		
		try {
			proxy.iniciarPartida(2);
			successful = true;
		} catch (NaoConectadoException e) {
			JOptionPane.showMessageDialog(this, e.getMessage());
			e.printStackTrace();
			successful = false;
		}
		
		return successful;
	}
	
	@Override
	public void iniciarNovaPartida(Integer posicao) {
		battleScene.setPosicao(posicao);
		
		goToScene("BattleScene");
		
		// TODO Define Jogador1
		if (posicao == 1) {
			this.setPosicao(posicao);
			battleScene.getBattle().getAllies()[0].setPosicao(1);
			battleScene.getBattle().getAllies()[0].setMinhaVez(true);
			
			battleScene.getBattle().getAllies()[1].setPosicao(2);
			battleScene.getBattle().getAllies()[1].setMinhaVez(false);
			iniciarPartidaRedeJogador1();
		}
		
		// TODO Define Jogador2
		if (posicao == 2) {
			this.setPosicao(posicao);
			battleScene.getBattle().getAllies()[0].setPosicao(1);
			battleScene.getBattle().getAllies()[0].setMinhaVez(true);
			
			battleScene.getBattle().getAllies()[1].setPosicao(2);
			battleScene.getBattle().getAllies()[1].setMinhaVez(false);
		}

		System.out.println("Iniciar nova partida, posição: "+posicao);
	}
	
	
	public void enviarJogada(JogadaNetGames jogadaNetGames) {
		// Buffered Image não é serializavel
		
		try {
			// Envia a porra da jogada
			proxy.enviaJogada(jogadaNetGames);
			
			System.out.println("----------- Jogada Enviada -----------");
		} catch (NaoJogandoException e) {
			JOptionPane.showMessageDialog(this, e.getMessage());
			e.printStackTrace();
		}
	}
	
	@Override
	public void receberJogada(Jogada jogada) {
		System.out.println("----------- AQUI EU RECEBI JOGADA -----------");
		JogadaNetGames recebida = (JogadaNetGames) jogada;
		
		Battle battle = battleScene.getBattle();
		
		connectionSceneAble = recebida.isConnectionSceneAble();
		
		// Atualiza as coordenadas do mapCursor
		battleScene.setMapCursorX(recebida.getMapCursorX());
		battleScene.setMapCursorY(recebida.getMapCursorY());
		Point location = battleScene.getMap().getTiles()[recebida.getMapCursorX()][recebida.getMapCursorY()].getSprite().getLocation();
		battleScene.mapCursor.setLocation(location);
		
		ArrayList<Coordinate> moveRangeCoordinate = recebida.getMoveRangeCoordinate();
		
		Tile[][] tiles = battle.getMap().getTiles();
		

//		ArrayList<Tile> teste = battleScene.convertCoordinates(moveRangeCoordinate);
//		for (Tile tile : teste) {
//			tile.state = recebida.getTileState();
//		}
		
		// Atualiza ocupantes dos tiles
		for (int i = 0; i < tiles.length; i++) {
			for (int j = 0; j < tiles[i].length; j++) {
				// Pintar todos os tiles de uma só cor não tá rolando
//				tiles[i][j].state = recebida.getTileState();
				
				tiles[i][j].setOccupant(null);
				
				// Essa parte foi feita só pra funcionar
				if (recebida.getPrevMoveCoordinate() != null) {
					if (i == recebida.getPrevMoveCoordinate().getX() &&
						j == recebida.getPrevMoveCoordinate().getY()) {
						tiles[i][j].setOccupant(null);
						
						ArrayList<Tile> lolWat = new ArrayList<Tile>();
						lolWat.add(tiles[i][j]);
						battleScene.drawMap(lolWat);
					}					
				}
				
				if (tiles[i][j].getOccupant() == null) {
					tiles[i][j].getSprite().changeTile("Floor", null);
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getJogador1().getCoordinate().getX() &&
					j == recebida.getJogador1().getCoordinate().getY()) {
					tiles[i][j].setOccupant(recebida.getJogador1());
				
					if (recebida.getJogador1().isDead()) {
						tiles[i][j].setOccupant(null);
					}
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getJogador2().getCoordinate().getX() &&
					j == recebida.getJogador2().getCoordinate().getY()) {
						tiles[i][j].setOccupant(recebida.getJogador2());

						if (recebida.getJogador2().isDead()) {
							tiles[i][j].setOccupant(null);
						}
						
						ArrayList<Tile> lolWat = new ArrayList<Tile>();
						lolWat.add(tiles[i][j]);
						battleScene.drawMap(lolWat);
				}

				if (i == recebida.getInimigo1().getCoordinate().getX() &&
					j == recebida.getInimigo1().getCoordinate().getY()) {
						tiles[i][j].setOccupant(recebida.getInimigo1());

						if (recebida.getInimigo1().isDead()) {
							tiles[i][j].setOccupant(null);
						}
						
						ArrayList<Tile> lolWat = new ArrayList<Tile>();
						lolWat.add(tiles[i][j]);
						battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getInimigo2().getCoordinate().getX() &&
					j == recebida.getInimigo2().getCoordinate().getY()) {
					tiles[i][j].setOccupant(recebida.getInimigo2());
					
					if (recebida.getInimigo2().isDead()) {
						tiles[i][j].setOccupant(null);
					}
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getInimigo3().getCoordinate().getX() &&
					j == recebida.getInimigo3().getCoordinate().getY()) {
					tiles[i][j].setOccupant(recebida.getInimigo3());
					
					if (recebida.getInimigo3().isDead()) {
						tiles[i][j].setOccupant(null);
					}
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getInimigo4().getCoordinate().getX() &&
					j == recebida.getInimigo4().getCoordinate().getY()) {
					tiles[i][j].setOccupant(recebida.getInimigo4());
					
					if (recebida.getInimigo4().isDead()) {
						tiles[i][j].setOccupant(null);
					}
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
				
				if (i == recebida.getInimigo5().getCoordinate().getX() &&
					j == recebida.getInimigo5().getCoordinate().getY()) {
					tiles[i][j].setOccupant(recebida.getInimigo5());
					
					if (recebida.getInimigo5().isDead()) {
						tiles[i][j].setOccupant(null);
					}
					
					ArrayList<Tile> lolWat = new ArrayList<Tile>();
					lolWat.add(tiles[i][j]);
					battleScene.drawMap(lolWat);
				}
			}
		}
		
		// Tem que atualizar o mapa certinho
		
		// Atualiza os dois jogadores
		battle.getAllies()[0].setBaseCharacter(recebida.getJogador1());
		battle.getAllies()[1].setBaseCharacter(recebida.getJogador2());
		
		battle.getEnemies()[0].setBaseCharacter(recebida.getInimigo1());
		battle.getEnemies()[1].setBaseCharacter(recebida.getInimigo2());
		battle.getEnemies()[2].setBaseCharacter(recebida.getInimigo3());
		battle.getEnemies()[3].setBaseCharacter(recebida.getInimigo4());
		battle.getEnemies()[4].setBaseCharacter(recebida.getInimigo5());
		
		battle.setRoundOrder(recebida.getRoundOrder());
		battle.setActiveUnitIndex(recebida.getActiveUnitIndex());
		
		// Move ----------------------------------------------
		if (moveRangeCoordinate != null) {
			ArrayList<Tile> tilesArrayList = battleScene.convertCoordinates(moveRangeCoordinate);
			
			// Eu consegui recuperar os tiles a partir de suas coordenadas, mas essa porra não tá desenhando direito
			if (recebida.getTileState() == 0) {
				battleScene.hideRange(tilesArrayList);
			}
			
			if (recebida.getTileState() == 1) {
				battleScene.showMovementRange(tilesArrayList);
			}
			
			if (recebida.getTileState() == 2) {
				battleScene.showAttackRange(tilesArrayList);
			}
			
			if (recebida.getTileState() == 3) {
				battleScene.showRadiusRange(tilesArrayList);
			}
		}
		// ----------------------------------------------
		
		// Act ----------------------------------------------
		if (recebida.isShowDefender()) {
			battleScene.defender.updateText(recebida.getDefenderText());
			battleScene.defenderParameters.updateText(recebida.getDefenderParamText());
			
			battleScene.defender.show();
			battleScene.defenderParameters.show();
		} else {
			battleScene.defender.hide();
			battleScene.defenderParameters.hide();
		}
		
		if (recebida.isShowAccAndDamage()) {
			battleScene.accuracyAndDamage.updateText(recebida.getAccAndDamageText());
			
			battleScene.accuracyAndDamage.show();
		} else {
			battleScene.accuracyAndDamage.hide();
		}
		
		if (recebida.isShowInfo()) {
			battleScene.info.updateText(recebida.getInfoText());
			
			battleScene.info.show();
			
			// Pra esconder a info após 5 segundos
			Timer infoTimer = new Timer(5000, new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent e) {
					battleScene.info.hide();
				}
			});
			infoTimer.setRepeats(false);
			infoTimer.start();
		} else {
			battleScene.info.hide();
		}
		// ----------------------------------------------
		
		// TODO ESTA MERDA QUE ME TIRA O SONO E A VONTADE DE VIVER
		if (recebida.isPassarVez()) {
			// O jogador 2 está preso no aguardo do jogador1
			// Após o jogador 1 jogar ele fica preso na mesma espera
			// Os inimigos não estão enviando NADA para o netgames
			// battle.passTurn(); é a chave pra passar o turno
			
			System.out.println(battleScene.getPosicao()+" É minha vez!");
			battleScene.setMinhaVez(true);
		} else {
			System.out.println(battleScene.getPosicao()+" Não é minha vez!");
			battleScene.menu.hide();
			battleScene.menu.disable();
			battleScene.setMinhaVez(false);
		}
		
		// Está pulando 1 turno
		// Está travando os comandos em Agir após serem usados 1 única vez
		// Jogador 2 nem sequer consegue Agir pois já está travado
		// Dá de mandar lambdas pro netgames
		// O draw tá todo errado, mas só no Jogador 2
		if (recebida.isBotao()) {
//			Runnable qwe = () -> battleScene.commandAct();
			
			try {battleScene.passTurnBattleScene();}
			catch (InterruptedException e) {e.printStackTrace();}
		}
		
		battleScene.turnOrder.updateText((	battleScene.getBattle().getActiveUnitIndex() + 1) +" / "+
											battleScene.getBattle().getRoundOrder().length);
		battleScene.attacker.updateText(battleScene.getBattle().getActiveUnit().getName());
		battleScene.attackerParameters.updateText(	"HP: " + battleScene.getBattle().getActiveUnit().getTotalHp() + " / " + 
			battleScene.getBattle().getActiveUnit().getTotalHpMax() +
			"\nMP: " + battleScene.getBattle().getActiveUnit().getTotalMp() + " / " +
			battleScene.getBattle().getActiveUnit().getTotalMpMax());
		
		// Se a batalha não foi iniciada, inicie-a
		if (!battleScene.isBattleStart()) {
			battleScene.setBattleStart(true);
			
			try {battleScene.battleStart(	recebida.getRoundOrder(), 
											recebida.getEnemyChosenSkillReceived(),			// Envia o índice da skill escolhida
											recebida.getEnemyChosenFoeReceived(),			// Envia o índice do alvo escolhido
											recebida.getEnemyValidChosenReceived(),			// Envia o índice da posição válida escolhida
											recebida.isSelectedFoeIsInRange());}
			catch (InterruptedException e) {e.printStackTrace();}
		}
	}

	@Override
	public void finalizarPartidaComErro(String message) {
		JOptionPane.showMessageDialog(this, "Partida finalizada com erro");
		
	}

	@Override
	public void receberMensagem(String msg) {
		
		
	}
	
	@Override
	public void tratarConexaoPerdida() {
		JOptionPane.showMessageDialog(this, "Conexão foi perdida");
	}

	@Override
	public void tratarPartidaNaoIniciada(String message) {
		
		
	}
	
	public boolean desconectar() {
		boolean successful = false;
		
		try {
			proxy.desconectar();
			successful = true;
		}
		catch (NaoConectadoException e) {e.printStackTrace();}
		
		return successful;
	}

	public Proxy getProxy() {
		return proxy;
	}

	public int getPosicao() {
		return posicao;
	}

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