package com.rjconsultores.ventaboletos.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zkoss.util.resource.Labels;

import com.rjconsultores.ventaboletos.dao.OrgaoTramoDAO;
import com.rjconsultores.ventaboletos.dao.RutaDAO;
import com.rjconsultores.ventaboletos.dao.TramoDAO;
import com.rjconsultores.ventaboletos.dao.TramoServicioDAO;
import com.rjconsultores.ventaboletos.entidad.ClaseServicio;
import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.entidad.OrgaoConcedente;
import com.rjconsultores.ventaboletos.entidad.OrgaoTramo;
import com.rjconsultores.ventaboletos.entidad.Parada;
import com.rjconsultores.ventaboletos.entidad.ParadaSecuencia;
import com.rjconsultores.ventaboletos.entidad.ParadaSecuenciaCombinacaoLinha;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.entidad.RutaCombinacion;
import com.rjconsultores.ventaboletos.entidad.RutaEmpresa;
import com.rjconsultores.ventaboletos.entidad.RutaSecuencia;
import com.rjconsultores.ventaboletos.entidad.Tramo;
import com.rjconsultores.ventaboletos.entidad.TramoServicio;
import com.rjconsultores.ventaboletos.entidad.Via;
import com.rjconsultores.ventaboletos.exception.BusinessException;
import com.rjconsultores.ventaboletos.service.RutaCombinacionService;
import com.rjconsultores.ventaboletos.service.RutaEmpresaService;
import com.rjconsultores.ventaboletos.service.RutaSecuenciaService;
import com.rjconsultores.ventaboletos.service.RutaService;
import com.rjconsultores.ventaboletos.service.TramoRutaService;
import com.rjconsultores.ventaboletos.service.TramoService;
import com.rjconsultores.ventaboletos.service.TramoServicioService;
import com.rjconsultores.ventaboletos.utilerias.DateUtil;
import com.rjconsultores.ventaboletos.utilerias.HoraSistema;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;
import com.rjconsultores.ventaboletos.vo.esquemaoperacional.RutaTramoVO;
import com.rjconsultores.ventaboletos.vo.esquemaoperacional.SecuenciaRutaTramoCoeficienteVO;
import com.rjconsultores.ventaboletos.vo.esquemaoperacional.SecuenciaRutaTramoVO;

@Service("tramoRutaService")
public class TramoRutaServiceImpl implements TramoRutaService {

	private static final Logger log = LogManager.getLogger(TramoRutaServiceImpl.class);
	@Autowired
	private RutaCombinacionService rutaCombinacionService;
	@Autowired
	private RutaSecuenciaService rutaSecuenciaService;
	@Autowired
	private RutaEmpresaService rutaEmpresaService;
	@Autowired
	private RutaService rutaService;
	@Autowired
	private TramoService tramoService;
	@Autowired
	private TramoServicioService tramoServicioService;
	@Autowired
	private RutaDAO rutaDAO;
	@Autowired
	private OrgaoTramoDAO orgaoTramoDAO;
	@Autowired
	private TramoServicioDAO tramoServicioDAO;
	@Autowired
	private TramoDAO tramoDAO;

	/**
	 * Hace la validación de los datos del rutaTramoVO
	 * 
	 * @param rutaTramoVO
	 * @throws BusinessException
	 */
	private void validarGenerarTramosRutas(RutaTramoVO rutaTramoVO) throws BusinessException {
		if (rutaTramoVO.getLsSecuenciaRutaTramoVO().isEmpty()) {
			throw new BusinessException("tramoRutaServiceImpl.msg.secuenciaObligatorio");
		}
		if ((rutaTramoVO.getPrefixo() == null) || (rutaTramoVO.getPrefixo().isEmpty())) {
			throw new BusinessException("tramoRutaServiceImpl.msg.prefixoObligatorio");
		}
		if (rutaTramoVO.getLsEmpresa().isEmpty()) {
			throw new BusinessException("tramoRutaServiceImpl.msg.empresaObligatoria");
		}

		if (rutaTramoVO.getLsCombinacionRutaTramoVO().isEmpty()) {
			throw new BusinessException("tramoRutaServiceImpl.msg.combinacionObligatorio");
		}
		for (SecuenciaRutaTramoCoeficienteVO s : rutaTramoVO.getLsCombinacionRutaTramoVO()) {
			if (s.getTramoId() != null) {
				continue;
			}
			if ((s.getOrigen() == null) || (s.getDestino() == null) || (s.getVia() == null)
					|| (s.getKmReal() == null) || (s.getSecuencia() == null) || (s.getTiempoRecorrido() == null)  
					|| ((rutaTramoVO.getOrgaoConcedente() == null || !rutaTramoVO.getOrgaoConcedente().getOrgaoConcedenteId().equals(21) 
						&& ((s.getCoeficienteTarifa1() == null) || (s.getKmCoeficiente1() == null) || (s.getKmCoeficiente1() == BigDecimal.ZERO))))) {

				throw new BusinessException("tramoRutaServiceImpl.msg.cambosObligatoriosCombinacion", new Object[] { s.getOrigen(), s.getDestino() });
			}
		}
		for (SecuenciaRutaTramoVO s : rutaTramoVO.getLsSecuenciaRutaTramoVO()) {
			if (s.getTramoId() != null) {
				continue;
			}
			if ((s.getOrigen() == null) || (s.getDestino() == null) || (s.getVia() == null)
					|| (s.getKmReal() == null) || (s.getSecuencia() == null) || (s.getTiempoRecorrido() == null)) {
				throw new BusinessException("tramoRutaServiceImpl.msg.cambosObligatoriosSecuencia", new Object[] { s.getOrigen(),
						s.getDestino() });
			}
		}
	}

	@Transactional(rollbackFor = BusinessException.class)
	@Override
	public Ruta generarTramosRutas(RutaTramoVO rutaTramoVO, Boolean isRegreso) throws BusinessException {

		this.validarGenerarTramosRutas(rutaTramoVO);

		// Genero los tramos y tramos servicios de la combinacion que no existen
		// Aqui si genera tambien los tramos de la secuencia
		for (SecuenciaRutaTramoCoeficienteVO s : rutaTramoVO.getLsCombinacionRutaTramoVO()) {
			log.debug("Origen:" + s.getOrigen() + ";Destino:" + s.getDestino() + ";Via:" + s.getVia());
			boolean fueGenerado = generarDatos(s, rutaTramoVO.getClaseServicio(), rutaTramoVO.getOrgaoConcedente());
			log.debug("Fue Generado:" + fueGenerado);
		}

		// Genero la ruta
		Ruta ruta = new Ruta();
		ruta.setDescruta(rutaTramoVO.getDescRuta());
		ruta.setClaseServicio(rutaTramoVO.getClaseServicio());
		ruta.setOrgaoConcedente(rutaTramoVO.getOrgaoConcedente());
		ruta.setIndNombreObligatorio(rutaTramoVO.getSolicitaNombrePasajero());
		ruta.setIndSentidoIda(rutaTramoVO.getIndSentidoIda());
		ruta.setIndTelefoneObrigatorio(false);
		if (rutaTramoVO.getVentaHandHeld() == null) {
			ruta.setVentaOffLine(false);
		} else {
			ruta.setVentaOffLine(rutaTramoVO.getVentaHandHeld());
		}

		ruta.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
		ruta.setActivo(Boolean.TRUE);
		ruta.setFecmodif(new java.util.Date());
		ruta.setPrefixo(rutaTramoVO.getPrefixo());
		ruta.setNumRuta(rutaTramoVO.getNumRuta());
		//valores de tributação para ecf
		ruta.setIndTribPedagio(Boolean.FALSE);
		ruta.setIndTribTarifa(Boolean.TRUE);
		ruta.setIndTribTaxaEmbarque(Boolean.FALSE);
		ruta.setIndRutaInternacional(Boolean.FALSE);
		
		ruta = rutaDAO.suscribir(ruta);

		// Genero la secuencia de la ruta
		for (SecuenciaRutaTramoVO s : rutaTramoVO.getLsSecuenciaRutaTramoVO()) {
			RutaSecuencia rutaSecuencia = new RutaSecuencia();
			rutaSecuencia.setNumsecuencia(s.getSecuencia().shortValue());
			rutaSecuencia.setTramo(tramoService.obtenerTramotPorOrigemDestinoVia(s.getOrigen(), s.getDestino(), s.getVia()));
			rutaSecuencia.setCantKmEntradaSaida(s.getKmEntradaSaida());
			rutaSecuencia.setKmOriginal(s.getKmOriginal());
			
			 
			if(rutaSecuencia.getTramo() == null ){
				Tramo tramo = new Tramo();
				tramo.setDesctramo(tramoService.gerarDescripcionTramo(s.getOrigen(), s.getDestino(), s.getVia()));
				tramo.setOrigem(s.getOrigen());
				tramo.setDestino(s.getDestino());
				tramo.setKmReal(s.getKmReal());
				tramo.setKmEntradaSaida(s.getKmSaidaOrigen());
				tramo.setKmEntradaSaidaDestino(s.getKmEntradaDestino());
				tramo.setVia(s.getVia());
				tramo.setActivo(Boolean.TRUE);
				tramo.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
				tramo.setFecmodif(new java.util.Date());

				tramo = tramoDAO.suscribir(tramo);
				rutaSecuencia.setTramo(tramo);
			} else {
				Tramo tramo = rutaSecuencia.getTramo();
				tramo.setKmEntradaSaida(s.getKmSaidaOrigen());
				tramo.setKmEntradaSaidaDestino(s.getKmEntradaDestino());
				tramo = tramoDAO.actualizacion(tramo);
			}
			rutaSecuencia.setRuta(ruta);
			rutaSecuencia = rutaSecuenciaService.suscribir(rutaSecuencia);
		}

		for (SecuenciaRutaTramoCoeficienteVO s : rutaTramoVO.getLsCombinacionRutaTramoVO()) {
			RutaCombinacion rutaCombinacion = new RutaCombinacion();
			rutaCombinacion.setIndventa(s.getPermiteVenda() == null ? true : s.getPermiteVenda());
			rutaCombinacion.setRuta(ruta);
			rutaCombinacion.setTramo(tramoService.obtenerTramotPorOrigemDestinoVia(s.getOrigen(), s.getDestino(), s.getVia()));

			rutaCombinacion = rutaCombinacionService.suscribir(rutaCombinacion);
		}

		for (Empresa e : rutaTramoVO.getLsEmpresa()) {
			RutaEmpresa rutaEmpresa = new RutaEmpresa();
			rutaEmpresa.setEmpresa(e);
			rutaEmpresa.setActivo(Boolean.TRUE);
			rutaEmpresa.setFecmodif(Calendar.getInstance().getTime());
			rutaEmpresa.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
			rutaEmpresa.setRuta(ruta);

			rutaEmpresa = rutaEmpresaService.suscribir(rutaEmpresa);
		}

		return ruta;
	}

	@Override
	public RutaTramoVO generarRutaRegreso(RutaTramoVO rutaTramoVO) {
		RutaTramoVO rutaTramoRegresoVO = new RutaTramoVO();
		rutaTramoRegresoVO.setClaseServicio(rutaTramoVO.getClaseServicio());
		rutaTramoRegresoVO.setOrgaoConcedente(rutaTramoVO.getOrgaoConcedente());
		rutaTramoRegresoVO.setSolicitaNombrePasajero(rutaTramoVO.getSolicitaNombrePasajero());
		rutaTramoRegresoVO.setVentaHandHeld(rutaTramoVO.getVentaHandHeld() == null ? true : rutaTramoVO.getVentaHandHeld());
		rutaTramoRegresoVO.setLsEmpresa(rutaTramoVO.getLsEmpresa());

		if (rutaTramoVO.getIndSentidoIda()) {
			rutaTramoRegresoVO.setIndSentidoIda(Boolean.FALSE);
		} else {
			rutaTramoRegresoVO.setIndSentidoIda(Boolean.TRUE);
		}
		rutaTramoRegresoVO.setNumRuta(rutaTramoVO.getNumRuta());
		rutaTramoRegresoVO.setPrefixo(rutaTramoVO.getPrefixo());
		if (rutaTramoVO.getDescRuta() != null) {
			rutaTramoRegresoVO.setDescRuta(rutaTramoVO.getDescRuta());
		} else {
			rutaTramoRegresoVO.setDescRuta("-");
		}

		List<SecuenciaRutaTramoVO> lsSecuenciaRutaTramoRegresoVO = new ArrayList<SecuenciaRutaTramoVO>();
		List<SecuenciaRutaTramoVO> lsSecuenciaRutaTramoIdaVO = new ArrayList<SecuenciaRutaTramoVO>(rutaTramoVO.getLsSecuenciaRutaTramoVO());
		// Reordena a lista
		Collections.reverse(lsSecuenciaRutaTramoIdaVO);

		int secuencia = 0;

		Map<Parada, BigDecimal> kmEntradaSaida = rutaTramoVO.getParadasComKmEntradaSaida();
		
		for (SecuenciaRutaTramoVO s : lsSecuenciaRutaTramoIdaVO) {
			TramoServicio tramoServicio = tramoServicioService.buscar(s.getDestino(), s.getOrigen(), s.getVia(), rutaTramoVO.getClaseServicio());
			if (tramoServicio != null) {
				s.setTramoId(tramoServicio.getTramo().getTramoId());
				s.setTramoServicioId(tramoServicio.getTramoservicioId());

				lsSecuenciaRutaTramoRegresoVO.add(new SecuenciaRutaTramoVO(s.getDestino(), s.getOrigen(), s.getVia(), ++secuencia, s.getKmReal(), s.getTiempoRecorrido(),
						s.getTramoId(), s.getTramoServicioId(), rutaTramoRegresoVO.getNumRuta(), rutaTramoRegresoVO.getIndSentidoIda(), kmEntradaSaida.containsKey(s.getDestino()) ? kmEntradaSaida.get(s.getDestino()) : BigDecimal.ZERO ));
			} else {
				lsSecuenciaRutaTramoRegresoVO.add(new SecuenciaRutaTramoVO(s.getDestino(), s.getOrigen(), s.getVia(), ++secuencia, s.getKmReal(), s.getTiempoRecorrido(), rutaTramoRegresoVO.getNumRuta(), rutaTramoRegresoVO.getIndSentidoIda(),kmEntradaSaida.containsKey(s.getDestino()) ? kmEntradaSaida.get(s.getDestino()) : BigDecimal.ZERO ));
			}
		}
		rutaTramoRegresoVO.setLsSecuenciaRutaTramoVO(lsSecuenciaRutaTramoRegresoVO);

		List<SecuenciaRutaTramoCoeficienteVO> lsCombinacionRutaTramoRegresoVO = new ArrayList<SecuenciaRutaTramoCoeficienteVO>();
		List<SecuenciaRutaTramoCoeficienteVO> lsCombinacionTramoIdaVO = new ArrayList<SecuenciaRutaTramoCoeficienteVO>(rutaTramoVO.getLsCombinacionRutaTramoVO());
		Collections.sort(lsCombinacionTramoIdaVO, new Comparator<SecuenciaRutaTramoCoeficienteVO>() {

			@Override
			public int compare(SecuenciaRutaTramoCoeficienteVO o1, SecuenciaRutaTramoCoeficienteVO o2) {
				return o2.getSecuencia().compareTo(o1.getSecuencia());
			}
		});

		secuencia = 0;
		for (SecuenciaRutaTramoCoeficienteVO s : lsCombinacionTramoIdaVO) {
			TramoServicio tramoServicio = tramoServicioService.buscar(s.getDestino(), s.getOrigen(), s.getVia(), rutaTramoVO.getClaseServicio());
			if (tramoServicio != null) {
				s.setTramoId(tramoServicio.getTramo().getTramoId());
				s.setTramoServicioId(tramoServicio.getTramoservicioId());

				SecuenciaRutaTramoCoeficienteVO secuenciaRutaTramoCoeficienteVO = new SecuenciaRutaTramoCoeficienteVO(s.getDestino(), s.getOrigen(), s.getVia(), ++secuencia, s.getKmReal(), s.getTiempoRecorrido(),
						s.getTramoId(), s.getTramoServicioId(), rutaTramoRegresoVO.getNumRuta(), s.getKmSaidaOrigen());

				OrgaoTramo orgaoTramo = orgaoTramoDAO.buscar(s.getDestino().getParadaId(), s.getOrigen().getParadaId(), s.getVia().getViaId(), rutaTramoVO.getOrgaoConcedente().getOrgaoConcedenteId(), rutaTramoVO.getClaseServicio().getClaseservicioId());

				if (orgaoTramo != null) {
					secuenciaRutaTramoCoeficienteVO.setOrgaoTramoId(orgaoTramo.getOrgaoTramoId());

					secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa1(s.getCoeficienteTarifa1());
					secuenciaRutaTramoCoeficienteVO.setKmCoeficiente1(s.getKmCoeficiente1());

					if (s.getCoeficienteTarifa2() != null) {
						secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa2(s.getCoeficienteTarifa2());
						secuenciaRutaTramoCoeficienteVO.setKmCoeficiente2(s.getKmCoeficiente2());
					}
					
					if (s.getCoeficienteTarifa3() != null) {
						secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa3(s.getCoeficienteTarifa3());
						secuenciaRutaTramoCoeficienteVO.setKmCoeficiente3(s.getKmCoeficiente3());
					}
				}

				secuenciaRutaTramoCoeficienteVO.setPermiteVenda(s.getPermiteVenda());
				lsCombinacionRutaTramoRegresoVO.add(secuenciaRutaTramoCoeficienteVO);
			} else {
				SecuenciaRutaTramoCoeficienteVO secuenciaRutaTramoCoeficienteVO = new SecuenciaRutaTramoCoeficienteVO(s.getDestino(), s.getOrigen(), s.getVia(), ++secuencia, s.getKmReal(), s.getTiempoRecorrido(), rutaTramoRegresoVO.getNumRuta(), s.getKmSaidaOrigen());

				secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa1(s.getCoeficienteTarifa1());
				secuenciaRutaTramoCoeficienteVO.setKmCoeficiente1(s.getKmCoeficiente1());

				secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa2(s.getCoeficienteTarifa2());
				secuenciaRutaTramoCoeficienteVO.setKmCoeficiente2(s.getKmCoeficiente2());
				
				secuenciaRutaTramoCoeficienteVO.setCoeficienteTarifa3(s.getCoeficienteTarifa3());
				secuenciaRutaTramoCoeficienteVO.setKmCoeficiente3(s.getKmCoeficiente3());
				secuenciaRutaTramoCoeficienteVO.setPermiteVenda(s.getPermiteVenda());

				lsCombinacionRutaTramoRegresoVO.add(secuenciaRutaTramoCoeficienteVO);
			}

		}
		rutaTramoRegresoVO.setLsCombinacionRutaTramoVO(lsCombinacionRutaTramoRegresoVO);

		return rutaTramoRegresoVO;
	}

	@Override
	public List<SecuenciaRutaTramoCoeficienteVO> generarCombinacion(RutaTramoVO rutaTramoVO) {
		List<SecuenciaRutaTramoVO> lsSecuenciaRutaTramoVO = new ArrayList<SecuenciaRutaTramoVO>(rutaTramoVO.getLsSecuenciaRutaTramoVO());
		List<SecuenciaRutaTramoCoeficienteVO> lsSecuenciaCombinacionRutaTramoVO = new ArrayList<SecuenciaRutaTramoCoeficienteVO>();

		Collections.sort(lsSecuenciaRutaTramoVO);

		int secuencia = 0;
		List<SecuenciaRutaTramoVO> listSecuenciaCalculada = new ArrayList<SecuenciaRutaTramoVO>(lsSecuenciaRutaTramoVO);

		for (SecuenciaRutaTramoVO secuenciaInicial : lsSecuenciaRutaTramoVO) {
			Parada origen = secuenciaInicial.getOrigen();
			Parada destino = secuenciaInicial.getDestino();

			listSecuenciaCalculada.remove(secuenciaInicial);

			BigDecimal totalKmsReal = secuenciaInicial.getKmReal();
			totalKmsReal = totalKmsReal.add(secuenciaInicial.getKmSaidaOrigen().add(secuenciaInicial.getKmEntradaDestino()));

			HoraSistema totalTiempoRecorrido = secuenciaInicial.getTiempoRecorrido();

			Via via = secuenciaInicial.getVia();

			secuencia = criaSecuenciaRutaTramoCoeficienteVO(rutaTramoVO, lsSecuenciaCombinacionRutaTramoVO, secuencia, totalKmsReal, origen, destino, secuenciaInicial.getKmSaidaOrigen(), secuenciaInicial.getKmEntradaDestino(), totalTiempoRecorrido, via);

			for (SecuenciaRutaTramoVO secuenciaInterna : listSecuenciaCalculada) {
				destino = secuenciaInterna.getDestino();
				totalKmsReal = totalKmsReal.add(secuenciaInterna.getKmReal());
				totalKmsReal = totalKmsReal.add(secuenciaInterna.getKmEntradaDestino());
				totalKmsReal = totalKmsReal.add(secuenciaInterna.getKmSaidaOrigen());
				totalTiempoRecorrido = totalTiempoRecorrido.sumar(secuenciaInterna.getTiempoRecorrido());

				secuencia = criaSecuenciaRutaTramoCoeficienteVO(rutaTramoVO, lsSecuenciaCombinacionRutaTramoVO, secuencia, totalKmsReal, origen, destino, secuenciaInicial.getKmSaidaOrigen(), secuenciaInterna.getKmEntradaDestino(), totalTiempoRecorrido, via);
			}
		}

		return lsSecuenciaCombinacionRutaTramoVO;
	}

	private int criaSecuenciaRutaTramoCoeficienteVO(RutaTramoVO rutaTramoVO, List<SecuenciaRutaTramoCoeficienteVO> lsSecuenciaCombinacionRutaTramoVO, int secuencia, BigDecimal totalKmsReal, Parada origen, Parada destino, BigDecimal kmSaidaOrigen, BigDecimal kmEntradaDestino, HoraSistema totalTiempoRecorrido, Via via) {
		SecuenciaRutaTramoCoeficienteVO nuevaSecuencia = new SecuenciaRutaTramoCoeficienteVO(origen, destino, via, ++secuencia,
				totalKmsReal, totalTiempoRecorrido, rutaTramoVO.getNumRuta());

		atualizandoSecuenciaPorViaExistente(rutaTramoVO, origen, destino, via, nuevaSecuencia);

		nuevaSecuencia.setKmSaidaOrigen(kmSaidaOrigen);
		nuevaSecuencia.setKmEntradaDestino(kmEntradaDestino);
		nuevaSecuencia.setNumRuta(rutaTramoVO.getNumRuta());
		lsSecuenciaCombinacionRutaTramoVO.add(nuevaSecuencia);
		
		log.debug("Origen:" + nuevaSecuencia.getOrigen() + ";Destino:" + nuevaSecuencia.getDestino() + ";Via:"
				+ nuevaSecuencia.getVia() + ";KmsReal:" + nuevaSecuencia.getKmReal() + ";hora:" + nuevaSecuencia.getTiempoRecorrido());
		return secuencia;
	}

	private void atualizandoSecuenciaPorViaExistente(RutaTramoVO rutaTramoVO, Parada origen, Parada destino, Via via, SecuenciaRutaTramoCoeficienteVO nuevaSecuencia) {
		if (via != null) {
			atualizandoSecuenciaPorTramosExistente(rutaTramoVO, origen, destino, via, nuevaSecuencia);
			atualizandoSecuenciaPorOrgaoTramosExistentes(rutaTramoVO, origen, destino, via, nuevaSecuencia);
		}
	}

	private void atualizandoSecuenciaPorOrgaoTramosExistentes(RutaTramoVO rutaTramoVO, Parada origen, Parada destino, Via via, SecuenciaRutaTramoCoeficienteVO nuevaSecuencia) {
		OrgaoTramo orgaoTramo = orgaoTramoDAO.buscar(origen.getParadaId(), destino.getParadaId(), via.getViaId(), rutaTramoVO.getOrgaoConcedente().getOrgaoConcedenteId(), rutaTramoVO.getClaseServicio().getClaseservicioId());
		if (orgaoTramo != null) {
			nuevaSecuencia.setCoeficienteTarifa1(orgaoTramo.getCoeficienteTarifa1());
			nuevaSecuencia.setKmCoeficiente1(orgaoTramo.getKmCoeficiente1());
			nuevaSecuencia.setCoeficienteTarifa2(orgaoTramo.getCoeficienteTarifa2());
			nuevaSecuencia.setKmCoeficiente2(orgaoTramo.getKmCoeficiente2());
			nuevaSecuencia.setCoeficienteTarifa3(orgaoTramo.getCoeficienteTarifa3());
			nuevaSecuencia.setKmCoeficiente3(orgaoTramo.getKmCoeficiente3());
			nuevaSecuencia.setOrgaoTramoId(orgaoTramo.getOrgaoTramoId());
		} else if (IsClasseAlterada(rutaTramoVO)) {
			orgaoTramo = orgaoTramoDAO.buscar(origen.getParadaId(), destino.getParadaId(), via.getViaId(), rutaTramoVO.getOrgaoConcedente().getOrgaoConcedenteId(), rutaTramoVO.getClaseServicioRutaOrigem().getClaseservicioId());
			if (orgaoTramo != null) {
				nuevaSecuencia.setKmCoeficiente1(orgaoTramo.getKmCoeficiente1());
				nuevaSecuencia.setKmCoeficiente2(orgaoTramo.getKmCoeficiente2());
				nuevaSecuencia.setKmCoeficiente3(orgaoTramo.getKmCoeficiente3());
				nuevaSecuencia.setOrgaoTramoId(orgaoTramo.getOrgaoTramoId());
				nuevaSecuencia.setClaseServicioRutaOrigemUtilizadaNaPesquisa(true);
			}
		}
		incluirCoeficiente(nuevaSecuencia, rutaTramoVO);
	}

	private void atualizandoSecuenciaPorTramosExistente(RutaTramoVO rutaTramoVO, Parada origen, Parada destino, Via via, SecuenciaRutaTramoCoeficienteVO nuevaSecuencia) {
		TramoServicio tramoServicio = tramoServicioService.buscar(origen, destino, via, rutaTramoVO.getClaseServicio());
		if (tramoServicio != null) {
			nuevaSecuencia.setKmReal(tramoServicio.getTramo().getKmReal());
			nuevaSecuencia.setTiempoRecorrido(new HoraSistema(tramoServicio.getTiemporecorrido()));
			nuevaSecuencia.setTramoId(tramoServicio.getTramo().getTramoId());
			nuevaSecuencia.setTramoServicioId(tramoServicio.getTramoservicioId());
		} else if (IsClasseAlterada(rutaTramoVO)) {
			tramoServicio = tramoServicioService.buscar(origen, destino, via, rutaTramoVO.getClaseServicioRutaOrigem());
			if (tramoServicio != null) {
				nuevaSecuencia.setKmReal(tramoServicio.getTramo().getKmReal());
				nuevaSecuencia.setTiempoRecorrido(new HoraSistema(tramoServicio.getTiemporecorrido()));
				nuevaSecuencia.setTramoId(tramoServicio.getTramo().getTramoId());
				nuevaSecuencia.setTramoServicioId(tramoServicio.getTramoservicioId());
			}
		}
	}

	private boolean IsClasseAlterada(RutaTramoVO rutaTramoVO) {
		return rutaTramoVO.getClaseServicioRutaOrigem() != null && rutaTramoVO.getClaseServicio().getClaseservicioId()!=rutaTramoVO.getClaseServicioRutaOrigem().getClaseservicioId();
	}

	private void incluirCoeficiente(SecuenciaRutaTramoCoeficienteVO nuevaSecuencia, RutaTramoVO rutaTramoVO) {
		if (nuevaSecuencia.getKmCoeficiente1() == null && rutaTramoVO.getCoeficienteDefault1() != null) {
			nuevaSecuencia.setKmCoeficiente1(nuevaSecuencia.getKmReal());
		}
		if (nuevaSecuencia.getKmCoeficiente2() == null && rutaTramoVO.getCoeficienteDefault2() != null) {
			nuevaSecuencia.setKmCoeficiente2(nuevaSecuencia.getKmReal());
		}
		if (nuevaSecuencia.getKmCoeficiente3() == null && rutaTramoVO.getCoeficienteDefault3() != null) {
			nuevaSecuencia.setKmCoeficiente3(nuevaSecuencia.getKmReal());
		}
	}
	
	@Override
	public List<SecuenciaRutaTramoCoeficienteVO> generarCombinacionTrechoRuta(RutaTramoVO rutaTramoVO, List<ParadaSecuencia> secuenciasDestino) {
		// una nueva lista que va a ser ordenada. Con eso, no afecta la lista original
		
		List<SecuenciaRutaTramoCoeficienteVO> lsSecuenciaCombinacionRutaTramoVO = new ArrayList<SecuenciaRutaTramoCoeficienteVO>();
		
		List<ParadaSecuenciaCombinacaoLinha> secuencias = rutaTramoVO.getLsParadaSecuenciaCombinacaoLinhas(); 
		
		for (ParadaSecuenciaCombinacaoLinha secuencia: secuencias){		
			Parada origem = secuencia.getOrigem().getParada();
			Via via = secuencia.getVia();
			
			BigDecimal totalKmsReal = BigDecimal.ZERO;
			
			BigDecimal kmsRealOrigem = secuencia.getKmReal() == null || secuencia.getKmReal().isEmpty() ? BigDecimal.ZERO : 
				new BigDecimal(secuencia.getKmReal()); 
	
			totalKmsReal = totalKmsReal.add(kmsRealOrigem);
			HoraSistema tempoTotalOrigemDestino = secuencia.getTempoReal().isEmpty() ? new HoraSistema() : new HoraSistema(DateUtil.getDataTempoDecorrido(secuencia.getTempoReal()));;
			
			for (ParadaSecuencia secuenciaDestino : secuenciasDestino) {
				
				//O processo não é realizado na ordem inversa (em que a sequencia da origem é maior que a sequencia de seus destinos), considerando que esse é o processo inversa a ida ou a volta.
				if (secuencia.getOrigem().getSecuencia() >= secuenciaDestino.getSecuencia()){
					continue;
				}
				
				Parada destino = secuenciaDestino.getParada();
				
				//A primeira seguencia sempre será a origem
				//A segunda sequencia sempre recebe a km e o tempo da origem
				if (secuenciaDestino.getSecuencia() > 2) {
					tempoTotalOrigemDestino = calcularTempoTotalDestino(secuencia, secuenciaDestino.getSecuencia());
					totalKmsReal = calcularKmDestino(secuencia, secuenciaDestino.getSecuencia());
				} 
				
				SecuenciaRutaTramoCoeficienteVO nuevaSecuencia = new SecuenciaRutaTramoCoeficienteVO(origem, destino, via,
						secuencia.getOrigem().getSecuencia().intValue(), totalKmsReal, tempoTotalOrigemDestino, rutaTramoVO.getNumRuta());

				atualizandoSecuenciaPorViaExistente(rutaTramoVO, origem, destino, via, nuevaSecuencia);
				

				nuevaSecuencia.setNumRuta(rutaTramoVO.getNumRuta());

				lsSecuenciaCombinacionRutaTramoVO.add(nuevaSecuencia);
			}
		}
		
		return lsSecuenciaCombinacionRutaTramoVO;
	}

	/**
	 * Genera los datos de tramo, tramo tiempo e coeficiente tarifa sino existen los datos
	 * 
	 * @param secuencia
	 * @param claseServicio
	 * @param orgaoConcedente
	 * @return
	 */
	private boolean generarDatos(SecuenciaRutaTramoCoeficienteVO secuencia, ClaseServicio claseServicio, OrgaoConcedente orgaoConcedente) {
		TramoServicio tramoServicio = tramoServicioDAO.buscar(secuencia.getOrigen(), secuencia.getDestino(), secuencia.getVia(), claseServicio);

		Tramo tramo = null;
		if (tramoServicio == null) {
			tramo = tramoService.buscar(secuencia.getOrigen(), secuencia.getDestino(), secuencia.getVia());
			if (tramo == null) {
				if(secuencia.getTramoId() != null){
					tramo = tramoService.obtenerID(secuencia.getTramoId());
				}else{
					tramo = new Tramo();
					tramo.setDesctramo(tramoService.gerarDescripcionTramo(secuencia.getOrigen(), secuencia.getDestino(), secuencia.getVia()));
					tramo.setOrigem(secuencia.getOrigen());
					tramo.setDestino(secuencia.getDestino());
					tramo.setKmReal(secuencia.getKmReal());
					tramo.setKmEntradaSaida(secuencia.getKmSaidaOrigen() == null ? BigDecimal.ZERO : secuencia.getKmSaidaOrigen());
					tramo.setKmEntradaSaidaDestino(secuencia.getKmEntradaDestino() == null ? BigDecimal.ZERO : secuencia.getKmEntradaDestino());
					tramo.setVia(secuencia.getVia());
					tramo.setActivo(Boolean.TRUE);
					tramo.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
					tramo.setFecmodif(new java.util.Date());
					
					tramo = tramoDAO.suscribir(tramo);
				}
			}
			tramoServicio = new TramoServicio();
			tramoServicio.setClaseServicio(claseServicio);
			tramoServicio.setTramo(tramo);
			tramoServicio.setTiemporecorrido(secuencia.getTiempoRecorrido().getFecha());

			tramoServicio = tramoServicioService.suscribir(tramoServicio);
			
			susCribrirOrgaoTramo(secuencia, claseServicio, orgaoConcedente, tramo);
			
			return true;
		}
		
		susCribrirOrgaoTramo(secuencia, claseServicio, orgaoConcedente, tramo);

		return false;
	}
	
	private void susCribrirOrgaoTramo(SecuenciaRutaTramoCoeficienteVO secuencia, ClaseServicio claseServicio, OrgaoConcedente orgaoConcedente, Tramo tramo) {
		
		if(tramo == null) {
			if(secuencia.getTramoId() != null){
				tramo = tramoService.obtenerID(secuencia.getTramoId());
			}
		}
		
		if ((secuencia.getClaseServicioRutaOrigemUtilizadaNaPesquisa() || secuencia.getOrgaoTramoId() == null )
				&& secuencia.getCoeficienteTarifa1() != null) {
			OrgaoTramo orgaoTramo = new OrgaoTramo();
			orgaoTramo.setClaseServicio(claseServicio);
			orgaoTramo.setOrgaoConcedente(orgaoConcedente);
			orgaoTramo.setTramo(tramo);

			orgaoTramo.setActivo(Boolean.TRUE);
			orgaoTramo.setUsuarioId(UsuarioLogado.getUsuarioLogado().getUsuarioId());
			orgaoTramo.setFecmodif(new java.util.Date());
			orgaoTramo.setCoeficienteTarifa1(secuencia.getCoeficienteTarifa1());
			orgaoTramo.setKmCoeficiente1(secuencia.getKmCoeficiente1());

			if (secuencia.getCoeficienteTarifa2() != null) {
				orgaoTramo.setCoeficienteTarifa2(secuencia.getCoeficienteTarifa2());
				orgaoTramo.setKmCoeficiente2(secuencia.getKmCoeficiente2());
			}
			
			if (secuencia.getCoeficienteTarifa3() != null) {
				orgaoTramo.setCoeficienteTarifa3(secuencia.getCoeficienteTarifa3());
				orgaoTramo.setKmCoeficiente3(secuencia.getKmCoeficiente3());
			}

			 orgaoTramoDAO.suscribir(orgaoTramo);
		}
	}
	
	@Transactional(rollbackFor = BusinessException.class)
	@Override
	public void generarTramosRutaExistente(RutaTramoVO rutaTramoVO, Ruta ruta) throws BusinessException {
		
		this.validarGenerarTramosRutas(rutaTramoVO);

		// Genero los tramos y tramos servicios de la combinacion que no existen
		// Aqui si genera tambien los tramos de la secuencia
		for (SecuenciaRutaTramoCoeficienteVO s : rutaTramoVO.getLsCombinacionRutaTramoVO()) {
			log.debug("Origen:" + s.getOrigen() + ";Destino:" + s.getDestino() + ";Via:" + s.getVia());
			boolean fueGenerado = generarDatos(s, rutaTramoVO.getClaseServicio(), rutaTramoVO.getOrgaoConcedente());
			log.debug("Fue Generado:" + fueGenerado);
		}

		// Genero la secuencia de la ruta
		for (SecuenciaRutaTramoVO s : rutaTramoVO.getLsSecuenciaRutaTramoVO()) {
			RutaSecuencia rutaSecuencia = new RutaSecuencia();
			rutaSecuencia.setNumsecuencia(s.getSecuencia().shortValue());
			rutaSecuencia.setTramo(tramoService.obtenerTramotPorOrigemDestinoVia(s.getOrigen(), s.getDestino(), s.getVia()));
			rutaSecuencia.setRuta(ruta);
			rutaSecuencia.setKmOriginal(rutaSecuencia.getTramo().getKmRealSemKmEntradaSaida());
			
			if (rutaSecuencia.getRuta() == null && rutaSecuencia.getRuta().getRutaId() == null) {
				rutaService.suscribir(rutaSecuencia.getRuta(), null);
			}
			rutaSecuencia = rutaSecuenciaService.suscribir(rutaSecuencia);
			
			if (rutaSecuencia.getRutasecuenciaId() == null){
				throw new BusinessException(Labels.getLabel("gerarCombinacionTramoRutaController.MSG.error.rutaSecuencia"));
			}
		}

		for (SecuenciaRutaTramoCoeficienteVO s : rutaTramoVO.getLsCombinacionRutaTramoVO()) {
			RutaCombinacion rutaCombinacion = new RutaCombinacion();
			rutaCombinacion.setIndventa(s.getPermiteVenda() == null ? true : s.getPermiteVenda());
			if (ruta.getIsClone() ) {
				Tramo tramoAtual = tramoService.obtenerTramotPorOrigemDestinoVia(s.getOrigen(), s.getDestino(), s.getVia());
				for (RutaCombinacion rcRuta : ruta.getRutaCombinacionList()) {
					if (rcRuta.getTramo() != null && tramoAtual != null && rcRuta.getTramo().getTramoId() == tramoAtual.getTramoId()) {
						rutaCombinacion.setIndventa(rcRuta.getIndventa() == null ? true : rcRuta.getIndventa());
					}
				}
			}
		
			
			rutaCombinacion.setRuta(ruta);
			rutaCombinacion.setTramo(tramoService.obtenerTramotPorOrigemDestinoVia(s.getOrigen(), s.getDestino(), s.getVia()));

			rutaCombinacion = rutaCombinacionService.suscribir(rutaCombinacion);
			
			if (rutaCombinacion.getRutacombinacionId() == null){
				throw new BusinessException(Labels.getLabel("gerarCombinacionTramoRutaController.MSG.error.rutaCombinacion"));
			}
		}
	}
	
	//calculo do tempo sempre considerando que o calculo deve ser feito desconsiderando o valor do tempo, 
	//inicializando com o tempo da origem e incrementando as sequencias anteriores
	private HoraSistema calcularTempoTotalDestino(ParadaSecuenciaCombinacaoLinha sequenciaOrigem, int numSeqAtual) {
		HoraSistema horaSistema = sequenciaOrigem.getTempoReal().isEmpty() ? new HoraSistema() : new HoraSistema(DateUtil.getDataTempoDecorrido(sequenciaOrigem.getTempoReal()));
		
		for (ParadaSecuencia destino: sequenciaOrigem.getDestinos()) {
			HoraSistema horaSistemaDestino = destino.getTempoReal().isEmpty() ? new HoraSistema() : 
				new HoraSistema(DateUtil.getDataTempoDecorrido(destino.getTempoReal()));
			
			//desconsidera o tempo para os destinos com sequencia inferior a origem, já que estes já foram calculados
			if (sequenciaOrigem.getOrigem().getSecuencia() > destino.getSecuencia()) {
				continue;
			}
			
			//faz o calculo para a sequencia do destino para qual o tempo está sendo calculado
			if (destino.getSecuencia() == numSeqAtual){
				break;
			}
			
			horaSistema = horaSistema.sumar(horaSistemaDestino);
		}
		
		return horaSistema;
		
	}
	
	//calculo da km sempre considerando que o calculo deve ser feito desconsiderando o valor da km, 
	//inicializando com o valor da km da origem e incrementando as sequencias anteriores
	private BigDecimal calcularKmDestino(ParadaSecuenciaCombinacaoLinha sequenciaOrigem, int numSeqAtual) {
		BigDecimal kmTotal= sequenciaOrigem.getKmReal() == null || sequenciaOrigem.getKmReal().isEmpty() ? BigDecimal.ZERO : 
			new BigDecimal(sequenciaOrigem.getKmReal());
		
		for (ParadaSecuencia destino: sequenciaOrigem.getDestinos()) {
			//desconsidera a km para os destinos com sequencia inferior a origem, já que estes já foram calculados
			if (sequenciaOrigem.getOrigem().getSecuencia() > destino.getSecuencia()) {
				continue;
			}
			
			//faz o calculo para a sequencia do destino para qual a km está sendo calculada
			if (destino.getSecuencia() == numSeqAtual){
				break;
			}
			
			BigDecimal kmDestino= destino.getKmReal() == null || destino.getKmReal().isEmpty() ? BigDecimal.ZERO : 
				new BigDecimal(destino.getKmReal()); 
			
			kmTotal = kmTotal.add(kmDestino);
		}
		
		return kmTotal;
	}
	
	@Transactional
	public void limparInconsistenciasTramo() {
		tramoServicioDAO.limparInconsistenciasTramo();
	}
}
