package com.rjconsultores.ventaboletos.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.rjconsultores.ventaboletos.dao.ParadaDAO;
import com.rjconsultores.ventaboletos.entidad.Parada;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.service.ConexionRutaCombinacionService;
import com.rjconsultores.ventaboletos.service.ParadaService;
import com.rjconsultores.ventaboletos.service.RutaCombinacionService;
import com.rjconsultores.ventaboletos.vo.conexion.OrdenarLocalidadesGeradas;
import com.rjconsultores.ventaboletos.vo.parada.ConexionCtrlVO;
import com.rjconsultores.ventaboletos.vo.parada.ConexionRutaCtrlVO;
import com.rjconsultores.ventaboletos.vo.parada.ParadaVOConexionRuta;

@Service("conexionRutaCombinacionFactory")
public class ConexionRutaCombinacionServiceImpl implements ConexionRutaCombinacionService {

	@Autowired
	private RutaCombinacionService rutaCombinacionService;
	
	@Autowired
	private ParadaService paradaService;

	private Map<Integer, Parada> criandoCacheLocalidades(List<Parada> list) {
		Map<Integer, Parada> cacheLocalidades = new HashMap<Integer, Parada>();
		for (Parada parada : list) {
			cacheLocalidades.put(parada.getParadaId(), parada);
		}
		return cacheLocalidades;
	}

	private void gerarCombinacao(LinkedList<List<Parada>> lists, List<Ruta> rutasEixoA, List<Ruta> rutasEixoB, List<Ruta> rutasEixoC, List<ParadaVOConexionRuta> localidadesGeradas, List<ConexionCtrlVO> conexoesCtrl, Map<Integer, Parada> cacheLocalidades, Boolean isConexaoPorAgrupamento) {


		Set<String> combinacoes = new TreeSet<String>();

		for (Parada s : lists.removeFirst())
			combinacoes.add(s.getParadaId().toString());

		while (!lists.isEmpty()) {
			List<Parada> next = lists.removeFirst();
			Set<String> novasCombinacoes = new TreeSet<String>();
			for (String s1 : combinacoes) {
				for (Parada s2 : next) {
					novasCombinacoes.add(s1 + ";" + s2.getParadaId());
				}
			}
			combinacoes = novasCombinacoes;
		}
		//System.out.println(combinacoes);
		
		Set<String> rutasVendaA = rutaCombinacionService.rutasCombinacionVenda(rutasEixoA);
		Set<String> rutasVendaB = rutaCombinacionService.rutasCombinacionVenda(rutasEixoB);

		if(rutasEixoC == null) {
			gerarLocalidadesEixoAB(rutasEixoA, rutasEixoB, localidadesGeradas, combinacoes, conexoesCtrl, cacheLocalidades, rutasVendaA, rutasVendaB, isConexaoPorAgrupamento);
		}else {
			Set<String> rutasVendaC = rutaCombinacionService.rutasCombinacionVenda(rutasEixoC);
			gerarLocalidadesEixoABC(rutasEixoA, rutasEixoB,rutasEixoC, localidadesGeradas, combinacoes, conexoesCtrl, cacheLocalidades, rutasVendaA, rutasVendaB, rutasVendaC );
		}
		
		Collections.sort(localidadesGeradas, new OrdenarLocalidadesGeradas());
	}


	private void gerarLocalidadesEixoAB(List<Ruta> rutasEixoA, List<Ruta> rutasEixoB, List<ParadaVOConexionRuta> localidadesGeradas, Set<String> combinacoes, List<ConexionCtrlVO> conexoesCtrl, Map<Integer, Parada> cacheLocalidades, Set<String> rutasVendaA, Set<String> rutasVendaB, Boolean isConexaoPorAgrupamento) {
		short i = 1;
		String[] valoresCalculados;
		
		for (Ruta rutaEixoA : rutasEixoA) {			
			for (Ruta rutaEixoB : rutasEixoB) {
				for (String comb : combinacoes) {
					valoresCalculados = comb.split(";");
					if(valoresCalculados != null && valoresCalculados.length == 4) {
						Integer origemConexaoCtrl = Integer.parseInt(valoresCalculados[0]); 
						Integer destinoConexaoCtrl = Integer.parseInt(valoresCalculados[3]);
						if(origemConexaoCtrl == null || cacheLocalidades.get(origemConexaoCtrl) == null ||
						  destinoConexaoCtrl == null || cacheLocalidades.get(destinoConexaoCtrl) == null) {
							continue;
						}
						
						ConexionCtrlVO conexionCtrl = adicionarConexao(conexoesCtrl, origemConexaoCtrl, destinoConexaoCtrl);
						ConexionRutaCtrlVO conexionRutaCtrl = new ConexionRutaCtrlVO(rutaEixoA.getRutaId(), rutaEixoB.getRutaId(), null);
						
						ParadaVOConexionRuta trechoA = new ParadaVOConexionRuta(conexionCtrl.getGrupo(), cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[1])), rutaEixoA.getRutaId(), rutaEixoA.getNumRuta(), i, true,
								cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[3])),
								conexionCtrl, conexionRutaCtrl, rutaEixoA.getDescSentido());
						i++;
						
						ParadaVOConexionRuta trechoB = new ParadaVOConexionRuta(conexionCtrl.getGrupo(), cacheLocalidades.get(Integer.parseInt(valoresCalculados[2])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[3])), rutaEixoB.getRutaId(),  rutaEixoB.getNumRuta(), i, true,
								cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[3])),
								conexionCtrl, conexionRutaCtrl, rutaEixoB.getDescSentido());
						i++;
						
						adicionarLocalidadeGerada(conexionCtrl, localidadesGeradas, trechoA, trechoB, null, rutasVendaA, rutasVendaB,  new HashSet<String>(), isConexaoPorAgrupamento);
						
						i = 1;
					}
					
				}
			}
		}
		
		removerConexionCtrlInvalida(conexoesCtrl);
	}
	
	/**
	 * @param conexoesCtrl
	 * @param origemConexaoCtrl
	 * @param destinoConexaoCtrl
	 * @return
	 */
	private ConexionCtrlVO adicionarConexao(List<ConexionCtrlVO> conexoesCtrl, Integer origemConexaoCtrl, Integer destinoConexaoCtrl) {
		ConexionCtrlVO conexionCtrl = null;
		Integer grupo = 0;
		for (ConexionCtrlVO conexionCtrlTemp : conexoesCtrl) {
			if(conexionCtrlTemp.isOrigemDestinoIgual(origemConexaoCtrl, destinoConexaoCtrl)) {
				grupo++;
			}
		}

		if(conexionCtrl == null) {
			conexionCtrl = new ConexionCtrlVO(origemConexaoCtrl, destinoConexaoCtrl);
			conexionCtrl.setGrupo(grupo);
		}
		
		conexionCtrl.incrementarGrupo();
		
		conexoesCtrl.add(conexionCtrl);
		
		return conexionCtrl;
	}
	
	private void adicionarLocalidadeGerada(ConexionCtrlVO conexionCtrl, List<ParadaVOConexionRuta> localidadesGeradas, ParadaVOConexionRuta trechoA, ParadaVOConexionRuta trechoB, ParadaVOConexionRuta trechoC, Set<String> rutasVendaA, Set<String> rutasVendaB, Set<String> rutasVendaC, Boolean isConexaoPorAgrupamento) {
		boolean isTrechoAVende = false;
		boolean isTrechoBVende = false;
		
		if(isDadosNotNull(trechoA)) {
			isTrechoAVende = rutasVendaA == null ? false : rutasVendaA.contains(trechoA.getRutaId() + "-" + trechoA.getParadaOrigem().getParadaId() + "-" + trechoA.getParadaDestino().getParadaId());
		}
		
		if(isDadosNotNull(trechoB)) {
			isTrechoBVende = rutasVendaB == null ? false : rutasVendaB.contains(trechoB.getRutaId() + "-" + trechoB.getParadaOrigem().getParadaId() + "-" + trechoB.getParadaDestino().getParadaId());
		}
		
		boolean isTrechoCVende = true;
		if(trechoC != null) {
			if(isDadosNotNull(trechoC)) {
				isTrechoCVende = rutasVendaC == null ? false : rutasVendaC.contains(trechoC.getRutaId() + "-" + trechoC.getParadaOrigem().getParadaId() + "-" + trechoC.getParadaDestino().getParadaId());
			} else {
				isTrechoCVende = false;
			}
		}
		
		if((isTrechoAVende && isTrechoBVende && isTrechoCVende) || Boolean.TRUE.equals(isConexaoPorAgrupamento)) {
			localidadesGeradas.add(trechoA);
			localidadesGeradas.add(trechoB);
			if(trechoC != null) {
				localidadesGeradas.add(trechoC);
			}
			conexionCtrl.setValida(true);
		} else {
			conexionCtrl.decrementarGrupo();
		}
	}

	private void gerarLocalidadesEixoABC(List<Ruta> rutasEixoA, List<Ruta> rutasEixoB, List<Ruta> rutasEixoC, List<ParadaVOConexionRuta> localidadesGeradas, Set<String> combinations, List<ConexionCtrlVO> conexoesCtrl, Map<Integer, Parada> cacheLocalidades, Set<String> rutasVendaA, Set<String> rutasVendaB, Set<String> rutasVendaC) {
		
		short i = 1;
		String[] valoresCalculados = null;
		for (Ruta rutaEixoA : rutasEixoA) {
			
			for (Ruta rutaEixoB : rutasEixoB) {
				for (Ruta rutaEixoC : rutasEixoC) {
					for (String comb : combinations) {
						valoresCalculados = comb.split(";");
						if(valoresCalculados != null && valoresCalculados.length == 5) {
							Integer origemConexaoCtrl = Integer.parseInt(valoresCalculados[0]); 
							Integer destinoConexaoCtrl = Integer.parseInt(valoresCalculados[4]);
							if(origemConexaoCtrl == null || cacheLocalidades.get(origemConexaoCtrl) == null ||
							   destinoConexaoCtrl == null || cacheLocalidades.get(destinoConexaoCtrl) == null) {
								continue;
							}
							
							ConexionCtrlVO conexionCtrl = adicionarConexao(conexoesCtrl, origemConexaoCtrl, destinoConexaoCtrl);
							ConexionRutaCtrlVO conexionRutaCtrl = new ConexionRutaCtrlVO(rutaEixoA.getRutaId(), rutaEixoB.getRutaId(), rutaEixoC.getRutaId());
							
							ParadaVOConexionRuta trechoA = new ParadaVOConexionRuta(conexionCtrl.getGrupo(), cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[1])), rutaEixoA.getRutaId(), rutaEixoA.getNumRuta(), i, true,
									cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[4])),
									conexionCtrl, conexionRutaCtrl, rutaEixoA.getDescSentido());
							i++;
							
							ParadaVOConexionRuta trechoB = new ParadaVOConexionRuta(conexionCtrl.getGrupo(), cacheLocalidades.get(Integer.parseInt(valoresCalculados[2])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[3])), rutaEixoB.getRutaId(), rutaEixoB.getNumRuta(), i, true,
									cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[4])),
									conexionCtrl, conexionRutaCtrl, rutaEixoB.getDescSentido());
							i++;
							
							ParadaVOConexionRuta trechoC = new ParadaVOConexionRuta(conexionCtrl.getGrupo(), cacheLocalidades.get(Integer.parseInt(valoresCalculados[3])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[4])), rutaEixoC.getRutaId(), rutaEixoC.getNumRuta(), i, true,
									cacheLocalidades.get(Integer.parseInt(valoresCalculados[0])), cacheLocalidades.get(Integer.parseInt(valoresCalculados[4])),
									conexionCtrl, conexionRutaCtrl, rutaEixoC.getDescSentido());
							i++;
							
							adicionarLocalidadeGerada(conexionCtrl, localidadesGeradas, trechoA, trechoB, trechoC, rutasVendaA, rutasVendaB, rutasVendaC, null);
							
							i = 1;
						}
					}
				}

			}
		}
		
		removerConexionCtrlInvalida(conexoesCtrl);
	}

	private void removerConexionCtrlInvalida(List<ConexionCtrlVO> conexoesCtrl) {
		List<ConexionCtrlVO> temp = new ArrayList<ConexionCtrlVO>(0);
		for (ConexionCtrlVO conexionCtrl : conexoesCtrl) {
			if(conexionCtrl.isValida()) {
				temp.add(conexionCtrl);
			}
		}
		conexoesCtrl.clear();
		conexoesCtrl.addAll(temp);
	}

	public void gerarCombinacionEntre2Eixos(List<Parada> listParada1, List<Ruta> rutasEixoA, List<Ruta> rutasEixoB, List<Parada> lsLocalidadesComuns, List<Parada> listParada4, List<Ruta> rutasEixoA2, List<Ruta> rutasEixoB2, List<ParadaVOConexionRuta> localidadesGeradas, List<ConexionCtrlVO> conexoesCtrl, Boolean isConexaoPorAgrupamento) {

		for (int i = 0; i < lsLocalidadesComuns.size(); i++) {

			List<Parada> listParada2 = Arrays.asList(lsLocalidadesComuns.get(i));
			List<Parada> listParada3 = Arrays.asList(lsLocalidadesComuns.get(i));

			LinkedList<List<Parada>> lists = new LinkedList<List<Parada>>();

			lists.add(listParada1);
			lists.add(listParada2);
			lists.add(listParada3);
			lists.add(listParada4);

			List<Parada> localidades = new ArrayList<Parada>();
			localidades.addAll(listParada1);
			localidades.addAll(listParada2);
			localidades.addAll(listParada3);
			localidades.addAll(listParada4);
			List<Parada> localidadesAux = new ArrayList<Parada>();
			localidadesAux.addAll(localidades);
			for (Parada parada : localidadesAux) {
				if(parada.getAgrupamentoParada() !=null) {
					List<Parada> paradaAgrupamentoList = paradaService.buscarPorAgrupamentoParadaId(parada.getAgrupamentoParada());	
					if(paradaAgrupamentoList!=null && !paradaAgrupamentoList.isEmpty()) {
						for (Parada paradaAgrupamento : paradaAgrupamentoList) {
							if(!localidades.contains(paradaAgrupamento)) {
								localidades.add(paradaAgrupamento);
							}
						}
					}
				}
			}

			Map<Integer, Parada> cacheLocalidades = criandoCacheLocalidades(localidades);
			gerarCombinacao(lists, rutasEixoA, rutasEixoB, null, localidadesGeradas, conexoesCtrl, cacheLocalidades, isConexaoPorAgrupamento);

		}

	}
	
	public List<Parada> getParadasByAgrupamentoId(Parada parada) {
		List<Parada> localidades = new ArrayList<Parada>();
		List<Parada> paradaAgrupamentoList = paradaService.buscarPorAgrupamentoParadaId(parada.getAgrupamentoParada());	
		if(paradaAgrupamentoList!=null && !paradaAgrupamentoList.isEmpty()) {
			for (Parada paradaAgrupamento : paradaAgrupamentoList) {
				if(!localidades.contains(paradaAgrupamento)) {
					localidades.add(paradaAgrupamento);
				}
			}
		}
		return localidades;
	}

	public void gerarCombinacionEntre3Eixos(List<Parada> listParada1, List<Ruta> rutasEixoA, List<Ruta> rutasEixoB, List<Parada> lsLocalidadesComuns, List<Parada> listParada4, List<Parada> listParadasC, List<Ruta> rutasEixoA2, List<Ruta> rutasEixoB2, List<Ruta> rutasEixoC, List<ParadaVOConexionRuta> localidadesGeradas, List<ConexionCtrlVO> conexoesCtrl) {
		
		for (int i = 0; i < lsLocalidadesComuns.size(); i++) {

			List<Parada> listParada2 = Arrays.asList(lsLocalidadesComuns.get(i));
			List<Parada> listParada3 = Arrays.asList(lsLocalidadesComuns.get(i));

			LinkedList<List<Parada>> lists = new LinkedList<List<Parada>>();

			lists.add(listParada1);
			lists.add(listParada2);
			lists.add(listParada3);
			lists.add(listParada4);
			lists.add(listParadasC);

			List<Parada> localidades = new ArrayList<Parada>();
			localidades.addAll(listParada1);
			localidades.addAll(listParada2);
			localidades.addAll(listParada3);
			localidades.addAll(listParada4);
			localidades.addAll(listParadasC);
			
			Map<Integer, Parada> cacheLocalidades = criandoCacheLocalidades(localidades);
			gerarCombinacao(lists, rutasEixoA, rutasEixoB, rutasEixoC, localidadesGeradas, conexoesCtrl, cacheLocalidades, null);

		}		
	}

	public List<ConexionRutaCtrlVO> extrairConexionRutaCtrl(List<ParadaVOConexionRuta> localidadesGeradas) {
		List<ConexionRutaCtrlVO> list = new ArrayList<ConexionRutaCtrlVO>(0);
		for (ParadaVOConexionRuta paradaVOConexionRuta : localidadesGeradas) {
			if(!list.contains(paradaVOConexionRuta.getConexionRutaCtrl())) {
				list.add(paradaVOConexionRuta.getConexionRutaCtrl());
			}
		}
		return list;
	}
	
	private boolean isDadosNotNull(ParadaVOConexionRuta trecho) {
		return trecho != null &&
			   trecho.getRutaId() != null &&
			   trecho.getParadaOrigem() != null &&
			   trecho.getParadaDestino() != null;
	}

}
