package process;

import java.util.ArrayList;
import java.util.Vector;

import org.apache.log4j.Logger;

import utils.Constantes;
import utils.Utilidades;
import actions.ResultadosAction;
import business.Grafo;
import business.Resultados;
import business.Vertice;
import core.BarraHerramientas;
import core.ModalAceptar;
import core.PanelEdicion;

public class CalculoProcess extends Thread {

	private static Logger logger = Logger.getLogger(CalculoProcess.class);
	private PanelEdicion panelEdicion;
	private ResultadosAction resultadosAction;
	private BarraHerramientas barraHerramientas;

	public CalculoProcess (PanelEdicion pEdicion, ResultadosAction res, BarraHerramientas barra) {
		panelEdicion = pEdicion;
		resultadosAction = res;
		barraHerramientas = barra;
	}

	@SuppressWarnings("unchecked")
	public void run() {
		Grafo grafo = panelEdicion.getGrafo();
		Integer numeroVertices = grafo.getVertices().size();
		int matrizDistancias[][];
		if(Resultados.getMatriz() == null) {
			matrizDistancias = new int[numeroVertices][numeroVertices];
			Utilidades.CalcularMatriz(matrizDistancias, grafo);
			Resultados.setMatriz(matrizDistancias);
		} else {
			matrizDistancias = Resultados.getMatriz();
		}
		boolean inconexo = false;
		for(int f = 0; f < matrizDistancias.length && !inconexo; f++) {
			for(int c = 0; c < matrizDistancias.length && !inconexo; c++)
				if((matrizDistancias[f][c] == 0 || matrizDistancias[f][c] == -1) && f != c) {
					inconexo = true;
				}
		}

		if (inconexo) {
			ModalAceptar modal = new ModalAceptar(panelEdicion.getMensajes(), Constantes.ERROR, "GrafoInconexo");
			modal.setVisible(true);
		} else {
			ArrayList<Vertice> vertices = grafo.getVertices();
			ArrayList<Integer[]> resultado = new ArrayList<Integer[]>();
			ArrayList<ArrayList<Integer>> conjuntosVistos = new ArrayList<ArrayList<Integer>>();
			try {
				long horaInicio = System.currentTimeMillis();

				ArrayList<Combinacion> pila = new ArrayList<Combinacion>();

				Vertice v = null;
				for(int i = 0; i < numeroVertices; i++) {
					v = vertices.get(i);
					Combinacion c = new Combinacion();
					c.procesando.add(new Integer(v.getId()));
					for(int j = 0; j < numeroVertices; j++)
						if(i != j) {
							v = vertices.get(j);
							c.pendientes.add(new Integer(v.getId()));
						}
					pila.add(c);
				}

				Combinacion combi = null;

				while(!pila.isEmpty()) {
					combi = pila.get(0);
					pila.remove(0);
					if(combi.procesando.size() <= numeroVertices && !Utilidades.contiene(conjuntosVistos, combi.procesando)) {
						conjuntosVistos.add((ArrayList<Integer>) combi.procesando.clone());
						for(int x = 0; x < vertices.size(); x++) {
							resultado.add(Utilidades.obtenerDistanciasResolutivas(x, (ArrayList<Integer>) combi.procesando.clone(), panelEdicion, Resultados.getMatriz()));
						}

						boolean hayRepetidos = Utilidades.hayRepetidos(resultado);
						if(!hayRepetidos) {
							Vector<ArrayList<Integer>> resultados = Resultados.getResultados();
							resultados.add((ArrayList<Integer>) combi.procesando.clone());
							barraHerramientas.getNumeroSoluciones().setText("(" + Integer.valueOf(resultados.size()).toString() + ")");
							resultadosAction.mostrarResultadosDimension(resultados.get(0));
						}
						resultado.clear();
						if(combi.pendientes.size() != 0)	{
							int max2 = combi.pendientes.size();
							for(int i = 0; i < max2; i++) {
								Combinacion combiAux = new Combinacion();
								combiAux.procesando = (ArrayList<Integer>)combi.procesando.clone();
								combiAux.procesando.add(combi.pendientes.get(i));
								for(int j = 0; j < max2; j++)
									if(i != j)
										combiAux.pendientes.add(combi.pendientes.get(j));

								pila.add(combiAux);
							}
						}
					} else
						if(combi.procesando.size() > numeroVertices)
							return;
				}

				long horaFin = System.currentTimeMillis();
				long segundos = (horaFin - horaInicio) / 1000L;
				logger.info("FIN CÁLCULO DIMENSIÓN MÉTRICA" +
						"\n\t\t\t Número de vértices: " + numeroVertices +
						"\n\t\t\t Número de aristas: " + grafo.getAristas().size() +
						"\n\t\t\t Resultados encontrados: " + Resultados.getResultados().size() +
						"\n\t\t\t Duración: " + segundos + " segundos");
			} catch(Exception exc) {
				exc.printStackTrace();
			}
		}
	}

	private class Combinacion {
		public ArrayList<Integer> procesando;
		public ArrayList<Integer> pendientes;

		Combinacion() {
			procesando = new ArrayList<Integer>();
			pendientes = new ArrayList<Integer>();
		}
	}
}