package cl.cromer.estructuras;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;

import java.net.URL;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.Scanner;

/**
 * Esta clase es para controlar todos la interfaz de ListaEnlazada.
 *
 * @author Chris Cromer
 */
public class ListaEnlazdaController implements Initializable {
	/**
	 * La caja para ingresar el valor.
	 */
	@FXML
	private TextFieldLimited valorLista;

	/**
	 * Donde poner el contenido de lista.
	 */
	@FXML
	private VBox contenidoLista;

	/**
	 * Donde poner el contenido de lista circular.
	 */
	@FXML
	private VBox contenidoListaCircular;

	/**
	 * Donde va el codigo a mostrar a la pantalla.
	 */
	@FXML
	private Text codigoLista;

	/**
	 * La escena donde está cosas graficas.
	 */
	private Scene scene;

	/**
	 * Donde está guardado los idiomas.
	 */
	private ResourceBundle resourceBundle;

	/**
	 * La lista enlazada usado en la aplicación.
	 */
	private ListaEnlazada listaEnlazada;

	/**
	 * La lista enlazada circular usado en la aplicación.
	 */
	private ListaEnlazadaCircular listaEnlazadaCircular;

	/**
	 * Tipo de lista enlazada a trabajar.
	 */
	private ListaEnlazada.Tipos listaEnlazadaTipos;

	/**
	 * Grafico rectangulos y lineas.
	 */
	private Grafico grafico;

	/**
	 * Colores por los dibjos.
	 */
	private Colores colores;

	/**
	 * Inicializar todos los datos y dibujar las graficas.
	 *
	 * @param location URL: El URL de fxml en uso.
	 * @param resourceBundle ResourceBundle: Tiene datos de idioma.
	 */
	@Override
	public void initialize(URL location, ResourceBundle resourceBundle) {
		this.resourceBundle = resourceBundle;

		scene = null;
	}

	/**
	 * Llenar la lista con numeros al azar.
	 */
	@FXML
	protected void botonLlenar() {
		if (scene == null) {
			initializeLista();
		}

		Random random = new Random();
		int maximo = 99;
		int minimo = 0;
		int rango = maximo - minimo + 1;

		if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
			for (listaEnlazada.size(); listaEnlazada.size() < 5; ) {
				int numero = random.nextInt(rango) + minimo;
				while (listaEnlazada.buscar(numero) != null) {
					numero = random.nextInt(rango) + minimo;
				}
				listaEnlazada.insertar(numero);
			}
		}
		else {
			for (listaEnlazadaCircular.size(); listaEnlazadaCircular.size() < 5; ) {
				int numero = random.nextInt(rango) + minimo;
				while (listaEnlazadaCircular.buscar(numero) != null) {
					numero = random.nextInt(rango) + minimo;
				}
				listaEnlazadaCircular.insertar(numero);
			}
		}
		generarGrafico();
	}

	/**
	 * Vaciar la lista de todos los valores.
	 */
	@FXML
	protected void botonVaciar() {
		if (scene == null) {
			initializeLista();
		}

		nuevaLista();
		generarGrafico();
	}

	/**
	 * Insertar un valor a la lista y mostrar el codigo en la pantalla.
	 */
	@FXML
	protected void botonInsertar() {
		if (scene == null) {
			initializeLista();
		}

		String tipo = getTipoString();

		// Mostrar el codigo
		String codigoTexto = new Scanner(getClass().getResourceAsStream("/cl/cromer/estructuras/code/listaEnlazada" + tipo + "/insertar")).useDelimiter("\\Z").next();
		codigoLista.setText(codigoTexto);

		if (valorLista.getText() != null && ! valorLista.getText().trim().equals("")) {
			try {
				boolean exito;
				if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
					exito = listaEnlazada.insertar(Integer.valueOf(valorLista.getText()));
				}
				else {
					exito = listaEnlazadaCircular.insertar(Integer.valueOf(valorLista.getText()));
				}
				if (exito) {
					valorLista.setText("");
					generarGrafico();
				}
				else {
					Main.mostrarError(resourceBundle.getString("listaLlaveExiste"), resourceBundle);
				}
			}
			catch (NumberFormatException exception) {
				// El error no es fatal, sigue
				Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
			}
		}
		else {
			Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
		}
	}

	/**
	 * Eliminar un valor de la lista si existe y mostrar el codigo en la pantalla.
	 */
	@FXML
	protected void botonEliminar() {
		if (scene == null) {
			initializeLista();
		}

		String tipo = getTipoString();

		// Mostrar el codigo
		String codigoTexto = new Scanner(getClass().getResourceAsStream("/cl/cromer/estructuras/code/listaEnlazada" + tipo + "/eliminar")).useDelimiter("\\Z").next();
		codigoLista.setText(codigoTexto);

		try {
			if (valorLista.getText() != null && ! valorLista.getText().trim().equals("")) {
				boolean exito;
				if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
					exito = listaEnlazada.eliminar(Integer.valueOf(valorLista.getText()));
				}
				else {
					exito = listaEnlazadaCircular.eliminar(Integer.valueOf(valorLista.getText()));
				}
				if (exito) {
					valorLista.setText("");
					generarGrafico();
				}
				else {
					Main.mostrarError(resourceBundle.getString("listaNoEsta"), resourceBundle);
				}
			}
			else {
				Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
			}
		}
		catch (NumberFormatException exception) {
			// El error no es fatal, sigue
			Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
		}
	}

	/**
	 * Buscar si existe una llave en la lista y mostrar el codigo en la pantalla
	 * Si existe la llave destacarla.
	 */
	@FXML
	protected void botonBuscar() {
		if (scene == null) {
			initializeLista();
		}

		String tipo = getTipoString();

		// Mostrar el codigo
		String codigoTexto = new Scanner(getClass().getResourceAsStream("/cl/cromer/estructuras/code/listaEnlazada" + tipo + "/buscar")).useDelimiter("\\Z").next();
		codigoLista.setText(codigoTexto);

		try {
			if (valorLista.getText() != null && ! valorLista.getText().trim().equals("")) {
				ListaEnlace listaEnlace;
				if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
					listaEnlace = listaEnlazada.buscar(Integer.valueOf(valorLista.getText()));
				}
				else {
					listaEnlace = listaEnlazadaCircular.buscar(Integer.valueOf(valorLista.getText()));
				}
				if (listaEnlace != null) {
					generarGrafico();
					grafico = new Grafico(scene);
					grafico.destacar("#caja_" + listaEnlace.getLlave(), Grafico.RECTANGULO);
					grafico.destacar("#texto_" + listaEnlace.getLlave(), Grafico.TEXTO);
				}
				else {
					Main.mostrarError(resourceBundle.getString("listaNoEsta"), resourceBundle);
				}
			}
			else {
				Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
			}
		}
		catch (NumberFormatException exception) {
			// El error no es fatal, sigue
			Main.mostrarError(resourceBundle.getString("listaNoValor"), resourceBundle);
		}
	}

	/**
	 * Crear una lista vacia.
	 */
	private void initializeLista() {
		scene = contenidoLista.getScene();
		grafico = new Grafico(scene);
		listaEnlazadaTipos = (ListaEnlazada.Tipos) scene.getUserData();
		nuevaLista();
	}

	/**
	 * Devolver la string de tipo de lista enlazada.
	 *
	 * @return String: El nombre del tipo de lista enlazada.
	 */
	private String getTipoString() {
		String tipo;
		switch (listaEnlazadaTipos.getTipo()) {
			case ListaEnlazada.Tipos.SIMPLE:
				tipo = "Simple";
				break;
			case ListaEnlazada.Tipos.CIRCULAR:
				tipo = "Circular";
				break;
			case ListaEnlazada.Tipos.DOBLEMENTE_ENLAZADA:
				tipo = "Doble";
				break;
			default:
				tipo = "Simple";
		}
		return tipo;
	}

	/**
	 * Poner los valores en el grafico.
	 */
	private void generarGrafico() {
		grafico.removerDestacar();
		colores = new Colores();
		contenidoLista.getChildren().clear();
		contenidoListaCircular.getChildren().clear();

		if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
			for (int i = 0; i < listaEnlazada.size(); i++) {
				ListaEnlace listaEnlace = listaEnlazada.getIndice(i);
				if (listaEnlazada.getTipo() == ListaEnlazada.Tipos.SIMPLE) {
					dibujarSimple(listaEnlace, false);
				}
				else if (listaEnlazada.getTipo() == ListaEnlazada.Tipos.DOBLEMENTE_ENLAZADA) {
					if (i != listaEnlazada.size() - 1) {
						dibujarDoble(listaEnlace, (i == 0));
					}
					else {
						dibujarSimple(listaEnlace, false);
					}
				}
				colores.siguinteColor();
			}
		}
		else {
			for (int i = 0; i < listaEnlazadaCircular.size(); i++) {
				ListaEnlace listaEnlace = listaEnlazadaCircular.getIndice(i);
				dibujarSimple(listaEnlace, (i == listaEnlazadaCircular.size() - 1));
				colores.siguinteColor();
			}
			if (listaEnlazadaCircular.size() > 0) {
				contenidoListaCircular.getChildren().addAll(Grafico.crearLineaCircular(listaEnlazadaCircular.size()));
			}
		}
	}

	/**
	 * Crear una nueva lista enlazada.
	 */
	private void nuevaLista() {
		if (listaEnlazadaTipos.getTipo() != ListaEnlazada.Tipos.CIRCULAR) {
			listaEnlazada = new ListaEnlazada();
			listaEnlazada.setTipo(listaEnlazadaTipos.getTipo());
		}
		else {
			listaEnlazadaCircular = new ListaEnlazadaCircular();
			listaEnlazadaCircular.setTipo(ListaEnlazada.Tipos.SIMPLE);
		}
	}

	/**
	 * Dibujarlo con una flecha.
	 *
	 * @param listaEnlace ListaEnlace: El listaEnlace que tiene la llave y valor.
	 * @param sinFlecha boolean: Verdad si necesita dibujar una flecha.
	 */
	private void dibujarSimple(ListaEnlace listaEnlace, boolean sinFlecha) {
		contenidoLista.getChildren().addAll(
				Grafico.crearCaja(colores, String.valueOf(listaEnlace.getLlave()), String.valueOf(listaEnlace.getLlave()))
		);
		if (! sinFlecha) {
			contenidoLista.getChildren().addAll(
					Grafico.crearLineaVertical(),
					Grafico.crearFlechaAbajo()
			);
		}
	}

	/**
	 * Dibujarlo con dos flechas.
	 *
	 * @param listaEnlace ListaEnlace: El listaEnlace que tiene la llave y valor.
	 * @param primer boolean: Verdad si es el primer elemento de la lista.
	 */
	private void dibujarDoble(ListaEnlace listaEnlace, boolean primer) {
		if (primer) {
			contenidoLista.getChildren().addAll(
					Grafico.crearFlechaArriba(),
					Grafico.crearLineaVertical()
			);
		}
		contenidoLista.getChildren().addAll(
				Grafico.crearCaja(colores, String.valueOf(listaEnlace.getLlave()), String.valueOf(listaEnlace.getLlave())),
				Grafico.crearFlechaArriba(),
				Grafico.crearLineaVertical(),
				Grafico.crearFlechaAbajo()
		);
	}
}
