package com.rjconsultores.ventaboletos.dao.hibernate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.criteria.CriteriaBuilder.Case;

import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.rjconsultores.ventaboletos.constantes.Constantes;
import com.rjconsultores.ventaboletos.dao.CalcularPeajeDAO;
import com.rjconsultores.ventaboletos.entidad.CasetaPeaje;
import com.rjconsultores.ventaboletos.entidad.CasetaPeajeExcepcion;
import com.rjconsultores.ventaboletos.entidad.ClaseServicio;
import com.rjconsultores.ventaboletos.entidad.ClasseIndicePeaje;
import com.rjconsultores.ventaboletos.entidad.ExcepcionPeaje;
import com.rjconsultores.ventaboletos.entidad.ExcepcionPeajeVigencia;
import com.rjconsultores.ventaboletos.entidad.OrgaoConcedente;
import com.rjconsultores.ventaboletos.entidad.Parada;
import com.rjconsultores.ventaboletos.entidad.PrecioFixoPedagio;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.service.OrgaoConcedenteService;
import com.rjconsultores.ventaboletos.service.PrecioFixoPedagioService;
import com.rjconsultores.ventaboletos.service.RutaCombinacionService;
import com.rjconsultores.ventaboletos.utilerias.ApplicationProperties;
import com.rjconsultores.ventaboletos.utilerias.CustomEnum;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;

@Repository("calcularPeajeDAO")
public class CalcularPeajeHibernateDAO extends GenericHibernateDAO<String, String>
		implements CalcularPeajeDAO {

	
	/**
	 * Este índice foi estabelecido conforme portaria do orgão anexa do mantis 8274
	 */
	private static final  float  INDICE_ORIGINAL_AGER_MT = 0.004895f;
	
	@Autowired
	private OrgaoConcedenteService orgaoConcedenteService;
	
	@Autowired
    private RutaCombinacionService rutaCombinacionService;
	
	@Autowired
	private PrecioFixoPedagioService precioFixoPedagioService;

	@Autowired
	public CalcularPeajeHibernateDAO(@Qualifier("sessionFactory") SessionFactory factory) {
		setSessionFactory(factory);
	}
	
	private static final Logger log = LogManager.getLogger(CalcularPeajeHibernateDAO.class);

	@Override
	public List<Ruta> buscarRutaWithCasetaPeajeFromOrgao(OrgaoConcedente oc) {
		StringBuilder sql = new StringBuilder()
				.append("select distinct r ")
				.append("from Ruta r, RutaCaseta rc, RutaSecuencia rs, CasetaPeaje cp ")
				.append("where rc.rutaSecuencia.rutasecuenciaId = rs.rutasecuenciaId ")
				.append("  and rc.casetaPeaje.casetaPeajeId = cp.casetaPeajeId ")
				.append("  and rs.ruta.rutaId = r.rutaId ");
		if (oc != null && oc.getOrgaoConcedenteId() != null) {
			sql.append(" and r.orgaoConcedente.orgaoConcedenteId = :orgaoConcedenteId ");
		}
		sql.append(" order by r.descruta ");

		Query query = getSession().createQuery(sql.toString());
		if (oc != null && oc.getOrgaoConcedenteId() != null) {
			query.setInteger("orgaoConcedenteId", oc.getOrgaoConcedenteId());
		}

		return query.list();
	}

	@Override
	public List<Object> buscarCasetasPeajeWithinTramo(Ruta ruta, Integer[] listCasetaPeaje) {
		StringBuilder sql = new StringBuilder()
				.append("with tramo_tmp as ( ")
				.append("  select t.origen_id as orig, t.destino_id as dest, t.tramo_id as tra, ")
				.append("    t.desctramo as tradesc, t.origen_id as traorig, t.destino_id as tradest, ")
				.append("    t.via_id as travia, t.cantkmreal as tracantkm, t.cantkmpagoconductor as tracantkmpago, ")
				.append("    t.activo as traactivo, t.fecmodif as trafecmodif, t.usuario_id as trausuario ")
				.append("  from ruta_combinacion rcom ")
				.append("    inner join ruta r on r.ruta_id = rcom.ruta_id ")
				.append("    inner join orgao_concedente oc on oc.orgaoconcedente_id = r.orgaoconcedente_id ")
				.append("    inner join tramo t on t.tramo_id = rcom.tramo_id ")
				.append("  where r.ruta_id = :rutaId  and rcom.activo =1 and r.activo=1 and oc.activo =1 and t.activo = 1 ")
				.append("), mytable as ( ")
				.append("  select eiorig.icms as icms, ")
				.append("    rseqorig.numsecuencia as origen_tramo_inicial_seq, rseqdest.numsecuencia as destino_tramo_final_seq, ")
				.append("    torig.tramo_id as tramo_orig, tdest.tramo_id as tramo_dest, ")
				.append("    tt.tra as tracomb, tt.orig as ttorig, tt.dest as ttdest, ")
				.append("    tt.tradesc as tracombdesc, tt.traorig as tracomborig, tt.tradest as tracombdest, ")
				.append("    tt.travia as tracombvia, tt.tracantkm as tracombcantkm, tt.tracantkmpago as tracombcantkmpago, ")
				.append("    tt.traactivo as tracombactivo, tt.trafecmodif as tracombfecmodif, tt.trausuario as tracombusuario, emp.NOMBEMPRESA, es.NOMBESTADO ")
				.append("  from ruta_secuencia rseqorig ")
				.append("    inner join tramo torig on torig.tramo_id = rseqorig.tramo_id and rseqorig.ruta_id = :rutaId ")
				.append("    inner join ruta_secuencia rseqdest on rseqorig.ruta_id = rseqdest.ruta_id ")
				.append("    inner join tramo tdest  on tdest.tramo_id = rseqdest.tramo_id and rseqdest.ruta_id = :rutaId ")
				.append("    inner join ruta_empresa re on re.ruta_id = :rutaId ")
				.append("    inner join empresa e on re.empresa_id = e.empresa_id ")
				.append("    inner join parada porig on torig.origen_id = porig.parada_id ")
				.append("    inner join ciudad corig on corig.ciudad_id = porig.ciudad_id ")
				.append("    inner join estado eorig on eorig.estado_id = corig.estado_id ")
				.append("    inner join empresa_imposto eiorig on eiorig.empresa_id = e.empresa_id and eiorig.estado_id = eorig.estado_id and eiorig.activo = 1 ")
				
				.append("          join empresa emp on emp.empresa_id = eiorig.empresa_id ")
				.append("          join estado es on eiorig.estado_id = es.estado_id ")
				
				.append("    inner join tramo_tmp tt on torig.origen_id = tt.orig and tdest.destino_id = tt.dest ")
				.append("    where  rseqorig.activo = 1 and torig.activo = 1 and rseqdest.activo = 1 and tdest.activo = 1 and re.activo = 1 and  e.activo = 1 and porig.activo = 1 and corig.activo = 1 and eorig.activo = 1  ")
				.append("), caseta_tramo_da_ruta as ( ")
				.append("  select rcas.rutacaseta_id as rutacasetaid, rcas.rutasecuencia_id as rutasecuenciaid, rcas.casetapeaje_id, ")
				.append("    rseq1.numsecuencia as numsecuencia, rseq1.tramo_id as tramoid, rseq2.numsecuencia as numsecuencia2, ")
				.append("    casp.casetapeaje_id as casetapeajeid, casp.desccaseta as desccaseta, casp.importeida as importeida, ")
				.append("    casp.importevuelta as importevuelta, casp.activo as caspactivo, casp.fecmodif as caspfecmodif, ")
				.append("    casp.usuario_id as caspusuario ")
				.append("  from ruta_caseta rcas ")
				.append("    inner join ruta_secuencia rseq1 on rcas.rutasecuencia_id = rseq1.rutasecuencia_id ")
				.append("    inner join ruta_secuencia rseq2 on rseq2.tramo_id = rseq1.tramo_id ")
				.append("    inner join tramo tra on tra.tramo_id = rseq1.tramo_id and rseq1.ruta_id = :rutaId ")
				.append("    inner join caseta_peaje casp on casp.casetapeaje_id = rcas.casetapeaje_id ")
				.append("    where rcas.activo =1 and  rseq1.activo =1 and   rseq2.activo =1 and  tra.activo =1 and casp.activo =1  ");
		         if(null!=listCasetaPeaje) {
		        	 sql.append( "and casp.casetapeaje_id in (" + StringUtils.join(listCasetaPeaje,",") + ") ");
		        	 
		         }
				sql.append(") ")
				.append("select distinct origen_tramo_inicial_seq, destino_tramo_final_seq, tramo_orig, tramo_dest, tracomb, ttorig, ttdest, ")
				.append("  tracombdesc, tracomborig, tracombdest, tracombvia, tracombcantkm, tracombcantkmpago, tracombactivo, ")
				.append("  tracombfecmodif, tracombusuario, rutacasetaid, rutasecuenciaid, casetapeaje_id, tramoid, casetapeajeid, ")
				.append("  desccaseta, importeida, importevuelta, caspactivo, caspfecmodif, caspusuario, icms, NOMBEMPRESA, NOMBESTADO ")
				.append("from mytable my ")
				.append("  inner join caseta_tramo_da_ruta ctr on ctr.numsecuencia between my.origen_tramo_inicial_seq and my.destino_tramo_final_seq ")
				.append("  order by tracomb ");

		Query query = getSession().createSQLQuery(sql.toString());
		query.setInteger("rutaId", ruta.getRutaId());
		query.setInteger("rutaId", ruta.getRutaId());
		query.setInteger("rutaId", ruta.getRutaId());
		query.setInteger("rutaId", ruta.getRutaId());		
	

		return query.list();
	}

	// pra cada ruta obter as combinacoes de tramos, cada tramo com sua lista de casetaPeaje
	public int[] gerarSQLInserirPeajes(List<Ruta> lsRuta, OrgaoConcedente orgao, boolean usaICMS,Integer[] listCasetaPeaje) throws Exception {
		boolean isOrgaoNull = orgao == null;
		List<String> updates = new ArrayList<String>();
		Set<PeajeVO> peajes = new HashSet<CalcularPeajeHibernateDAO.PeajeVO>();
		
		List<ClasseIndicePeaje> classeIndicePeajes = new ArrayList<ClasseIndicePeaje>();
		if(orgao != null){
			orgao = orgaoConcedenteService.obtenerID(orgao.getOrgaoConcedenteId());
			classeIndicePeajes = orgao.getClassesIndicePeaje();
			isOrgaoNull = orgao == null;
		}
		
		log.info("******  INICIO DO PROCESSO DE CALCULO DE PEDAGIO  ******");
		log.info("******  QTD DE LINHAS = "+lsRuta.size()+"******");
		int z = 0;
		for (Ruta ruta : lsRuta) {
			log.info("****** Linha "+(++z)+" de "+lsRuta.size()+" ******");
			log.info("****** LINHA : "+ruta.toString()+" ******");
			
			List<Object> lsObj = buscarCasetasPeajeWithinTramo(ruta,listCasetaPeaje);
			log.info("****** QTD COMBINAÇÕES DE TRECHOS x QTD PEDAGIOS POR TRECHO: "+lsObj.size()+" ******");
			
			if(isOrgaoNull){
				orgao = orgaoConcedenteService.obtenerID(ruta.getOrgaoConcedente().getOrgaoConcedenteId());
				classeIndicePeajes = orgao.getClassesIndicePeaje();
				String update = " update peaje set activo = 0 where ruta_id ="+ruta.getRutaId();
				if(null!=listCasetaPeaje) {
					update = update + " and casetapeaje_id in (" + StringUtils.join(listCasetaPeaje,",") + ") ";
				}
				updates.add(update);
			}

			List<PrecioFixoPedagio> lsPrecioFixoPedagio = precioFixoPedagioService.obtenerTodos();
			
			if (null != lsObj) {
				
				for (Object arrObj : lsObj) {
					
					if (((Object[]) arrObj)[27] == null) {
						throw new Exception (((Object[])arrObj)[28].toString() + " / " + ((Object[])arrObj)[29].toString());
					}
					BigDecimal bigKm = new BigDecimal(((Object[]) arrObj)[11].toString()) ;
					PeajeVO pvo = PeajeVO.create(ruta, orgao, (Object[]) arrObj, classeIndicePeajes, usaICMS, bigKm);
					// ATENCAO !!! 
					// neste 'FOR'  nao se pode abrir nenhum tipo de consulta no banco pois o 'lsObj' vem com milhares de registros
					// e se abrir uma sessao no banco para cada um dos registros, o banco trava.
					
					PrecioFixoPedagio pfp = new PrecioFixoPedagio();
					pfp.setOrgaoConcedenteId(orgao);
					pfp.setClasseId(ruta.getClaseServicio());
					CasetaPeaje pracaPedagio = new CasetaPeaje();
					pracaPedagio.setCasetaPeajeId(pvo.casetaPeajeId);
					pfp.setPracaPedagioId(pracaPedagio);
					
					for(PrecioFixoPedagio pfp2 : lsPrecioFixoPedagio){
						if(pfp.getOrgaoConcedenteId().getOrgaoConcedenteId() == -1 || (pfp2.getPracaPedagioId().getCasetaPeajeId().equals(pfp.getPracaPedagioId().getCasetaPeajeId())
							&& pfp2.getOrgaoConcedenteId().getOrgaoConcedenteId().equals(pfp.getOrgaoConcedenteId().getOrgaoConcedenteId())
							&& pfp2.getClasseId().getClaseservicioId().equals(pfp.getClasseId().getClaseservicioId()))
										){
							pvo.importePeaje = pfp2.getValorFixo();
							break;
						}
					}
					
					peajes.add(pvo);
				}
			}
			log.info("****** FIM LINHA : "+ruta.toString()+" . INSTRUCOES DE INSERT NO BANCO PRONTAS ******");
		}
		log.info("******  FIM DO PROCESSO DE CALCULO DE PEDAGIO  ******");

		StringBuilder updateSql = new StringBuilder();
		if(isOrgaoNull == false){
			updateSql = new StringBuilder()
			.append("update peaje set activo = 0 ")
			.append("where orgaoconcedente_id = ")
			.append(orgao.getOrgaoConcedenteId());
		}
		
		int[] result = {};
		Connection con = getSession().connection();
		try {
			Statement stmt = con.createStatement();
			con.setAutoCommit(false);
			if(!isOrgaoNull){
				stmt.addBatch(updateSql.toString());
			}else{
				for(String up : updates){
					stmt.addBatch(up);
				}
			}
			int i=0;
			
			for(PeajeVO peajeVO : peajes){
				i++;
				
				stmt.addBatch(peajeVO.toInsertQuery());
				stmt.addBatch(peajeVO.toUpdateExcepcionPeajeQuery());
				stmt.addBatch(peajeVO.toUpdateExcepcionPeajeVigenciaQuery());

				
				log.info("****  "+i+ "- de "+ peajes.size()+" de combinacoes de trecho X (praca pedagio por trecho)  ****");
				
				if(i%100 == 0){
					result = stmt.executeBatch();
					con.commit();
					stmt.close();
					stmt = con.createStatement();
				}
				
			}

			result = stmt.executeBatch();
			con.commit();
			stmt.close();
			recalcularExcecoesPedagio(peajes);
		}
		catch (SQLException e) {
			e.printStackTrace();
		}

		return result;
	}

	private Map<Integer, CasetaPeaje> buscaCasetas(Set<PeajeVO> pvo) {
		Set<Integer> ids = new LinkedHashSet<Integer>();
		Map<Integer, CasetaPeaje> casetas = new HashMap<Integer, CasetaPeaje>();

		for (PeajeVO p : pvo) {
			ids.add(p.casetaPeajeId);
		}

		if(!ids.isEmpty()){
			Query query = getSession()
					.createQuery("select c from CasetaPeaje c where c.casetaPeajeId in (:ids) ");
			query.setParameterList("ids", ids);

			for (CasetaPeaje c : (List<CasetaPeaje>) query.list()) {
				casetas.put(c.getCasetaPeajeId(), c);
			}
		}

		return casetas;
	}

	private ExcepcionPeaje getExcepcion(Integer rutaId, Integer origenId, Integer destinoId) {
		Query query = getSession().createQuery("select e from ExcepcionPeaje e where e.ruta.rutaId = :rutaId " +
				"and e.origem.paradaId = :origenId and e.destino.paradaId = :destinoId and e.activo = 1 ");

		query.setInteger("rutaId", rutaId);
		query.setInteger("origenId", origenId);
		query.setInteger("destinoId", destinoId);
		return (ExcepcionPeaje) query.uniqueResult();
	}

	private Parada getParada(Integer paradaId) {
		return (Parada) getSession().get(Parada.class, paradaId);
	}

	private Ruta getRuta(Integer rutaId) {
		return (Ruta) getSession().get(Ruta.class, rutaId);
	}

	@Transactional
	private void recalcularExcecoesPedagio(Set<PeajeVO> pvos) {
		Map<Integer, CasetaPeaje> casetas = buscaCasetas(pvos);
		for (PeajeVO peaje : pvos) {
			ExcepcionPeaje excepcion = null;
			CasetaPeaje caseta = casetas.get(peaje.casetaPeajeId);

			for (CasetaPeajeExcepcion cpe : caseta.getLsCasetaPeajeExcepcion()) {
				if (excepcion == null) {
					excepcion = new ExcepcionPeaje();
					excepcion.setActivo(true);
					excepcion.setDestino(getParada(peaje.destinoId));
					excepcion.setFecmodif(Calendar.getInstance().getTime());
					excepcion.setOrigem(getParada(peaje.origenId));
					excepcion.setRuta(getRuta(peaje.rutaId));
					excepcion.setUsuarioId(1);
				}
				ExcepcionPeajeVigencia epv = new ExcepcionPeajeVigencia();
				epv.setActivo(cpe.getActivo());
				epv.setExcepcionPeaje(excepcion);
				epv.setFecmodif(Calendar.getInstance().getTime());
				epv.setFecVentaFin(cpe.getFecVentaFin());
				epv.setFecVentaIni(cpe.getFecVentaIni());
				epv.setHoraFin(cpe.getHoraFin());
				epv.setHoraIni(cpe.getHoraIni());
				epv.setINDDOMINGO(cpe.getINDDOMINGO());
				epv.setINDJUEVES(cpe.getINDJUEVES());
				epv.setINDLUNES(cpe.getINDLUNES());
				epv.setINDMARTES(cpe.getINDMARTES());
				epv.setINDMIERCOLES(cpe.getINDMIERCOLES());
				epv.setINDSABADO(cpe.getINDSABADO());
				epv.setINDVIERNES(cpe.getINDVIERNES());
				epv.setPrecio(cpe.getPrecio());
				epv.setUsuarioId(1);
				epv.setCasetaPeaje(caseta);
				excepcion.getLsExcepcionPeajeVigencia().add(epv);
			}
			if (excepcion != null) {
				getSession().saveOrUpdate(excepcion);
			}
		}
	}

	private static class PeajeVO {

		private static final int HIGH_PRECISION = 10;
		private static final BigDecimal A_HUNDRED = new BigDecimal(100);
		private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

		private Integer rutaId;
		private Integer origenId;
		private Integer destinoId;
		private Integer orgaoId;
		private Integer casetaPeajeId;
		private Integer cantAsientos;
		private Integer cantEixos;
		private BigDecimal importePeaje;
		private BigDecimal indicePeaje;
		private Integer activo;
		private Date fecmodif;
		private Integer usuarioId;

		private static BigDecimal getCantEixos(Ruta ruta) {
			try {
				return new BigDecimal(ruta.getCantEixos());
			} catch (NullPointerException e) {
				throw new RuntimeException("rutaSemQtdEixos;" + ruta.getRutaId());
			}
		}

		private static BigDecimal getRutaCantAsientos(Ruta ruta) {
			try {
				return new BigDecimal(ruta.getCantAsientos());
			}
			catch (NullPointerException e) {
				throw new RuntimeException("rutaSemQtdAsientos;" + ruta.getRutaId());
			}
		}

		private static BigDecimal getImporte(Ruta ruta, Object[] obj) {
			if (ruta.getIndSentidoIda()) {
				return new BigDecimal(obj[22].toString());
			}
			else {
				return new BigDecimal(obj[23].toString());
			}
		}

		private static BigDecimal getIcms(Ruta ruta, Object[] obj) {
			return new BigDecimal(obj[27].toString())
					.divide(A_HUNDRED, HIGH_PRECISION, RoundingMode.HALF_UP);
		}

		// ((importe * rutaCantEixos) / (indice * rutaCantAsientos)) / ( 1 - icms)
		private static BigDecimal calculateImportePeajeANTT(BigDecimal importe, BigDecimal icms,
				BigDecimal indice, BigDecimal rutaCantEixos, BigDecimal rutaCantAsientos, boolean usaICMS) {

			if (ApplicationProperties.getInstance().isCustomHabilitado(CustomEnum.CALCULO_PEAJE_REUNIDAS_ANTT.getDescricao())) {  
				// (importe * rutaCantEixos) / (indice)
				return importe.multiply(rutaCantEixos)
						.divide(indice, HIGH_PRECISION, RoundingMode.HALF_UP);
			}
			else {
				if(!usaICMS){
					return importe.multiply(rutaCantEixos)
							.divide(indice.multiply(rutaCantAsientos), HIGH_PRECISION, RoundingMode.HALF_UP);
				}else{
					return importe.multiply(rutaCantEixos)
							.divide(indice.multiply(rutaCantAsientos), HIGH_PRECISION, RoundingMode.HALF_UP)
							.divide(BigDecimal.ONE.subtract(icms), HIGH_PRECISION, RoundingMode.HALF_UP);
				}
				
			}
		}
		
		private static BigDecimal calculateImportePeajeAGER(BigDecimal km, OrgaoConcedente orgao, BigDecimal importe,  
																BigDecimal rutaCantEixos, BigDecimal rutaCantAsientos) {
			BigDecimal indicePejeOrgao =  orgao!=null && orgao.getIndicePeaje() !=null ? orgao.getIndicePeaje() : new BigDecimal(INDICE_ORIGINAL_AGER_MT);
				// esse valor deveria vir do indice do orgao mas as casas decimais nao sao compativeis.. verificar se pode mudar a precisao do campo de indice..
			return validaValorMaxPedagioAGR(km.multiply(indicePejeOrgao).setScale(HIGH_PRECISION, RoundingMode.HALF_UP),
											indicePejeOrgao, importe, rutaCantEixos, rutaCantAsientos );		}
		
		/* -- RESOLUÇÃO NORMATIVA AGER/MT N.º 002/2023 --
		 *  O cálculo do valor a ser repassado ao passageiro não poderá ser superior ao
		 *	valor obtido mediante a aplicação da seguinte fórmula: 
		 *  𝐕𝐀𝐏 = ∑𝐕𝐏 𝐱 𝐧º𝐄 /	𝐋𝐎𝐓 𝐱 𝐈𝐀𝐏
		 *  VAP - Valor a ser pago por passageiro
		 *  ∑VP – Somatória do valor de pedágio no trecho percorrido pelo passageiro para 01 eixo simples
		 *  NºE – Número de Eixos
		 *  LOT - Lotação total do veículo padrão - 46 lugares (Edital de Licitação STCRIP)
		 *  IAP - Índice de Aproveitamento previsto na planilha tarifária - 60% (Edital de Licitação STCRIP)
		 * */
		private static BigDecimal validaValorMaxPedagioAGR(BigDecimal pedagio, BigDecimal indicePejeOrgao, BigDecimal importe, 
																BigDecimal rutaCantEixos, BigDecimal rutaCantAsientos) {
			BigDecimal valorCalculado = importe.multiply(rutaCantEixos)
												.divide(indicePejeOrgao.multiply(rutaCantAsientos),HIGH_PRECISION, RoundingMode.HALF_UP);
			return valorCalculado.min(pedagio);
		}
		
		// ((importe * rutaCantEixos) / (indice)) / ( 1 - icms)
		private static BigDecimal calculateImportePeajeARTESP(BigDecimal importe, BigDecimal icms,
				BigDecimal indice, BigDecimal rutaCantEixos, boolean usaICMS) {
			if(!usaICMS){
				return importe.multiply(rutaCantEixos)
						.divide(indice, HIGH_PRECISION, RoundingMode.HALF_UP);
			}else{
				return importe.multiply(rutaCantEixos)
						.divide(indice, HIGH_PRECISION, RoundingMode.HALF_UP)
						.divide(BigDecimal.ONE.subtract(icms), HIGH_PRECISION, RoundingMode.HALF_UP);
			}
			
		}

		private static BigDecimal calculateImportePeaje(Ruta ruta, OrgaoConcedente orgao, Object[] obj, BigDecimal indicePeaje, boolean usaICMS, BigDecimal km) {
			BigDecimal rutaCantEixos = getCantEixos(ruta);
			BigDecimal indice = indicePeaje;
			BigDecimal importe = getImporte(ruta, obj);
			BigDecimal icms = usaICMS ? getIcms(ruta, obj) : null;

			if (Constantes.ORGAOS_CONCEDENTES_CALCULO_ARTESP.contains(orgao.getOrgaoConcedenteId())) {
				return calculateImportePeajeARTESP(importe, icms, indice, rutaCantEixos, usaICMS);
			}
			if (Constantes.ORGAOS_CONCEDENTES_CALCULO_ANTT.contains(orgao.getOrgaoConcedenteId())) {
				return calculateImportePeajeANTT(importe, icms, indice, rutaCantEixos, getRutaCantAsientos(ruta), usaICMS);
			}
			if (Constantes.ORGAOS_CONCEDENTES_CALCULO_AGER.contains(orgao.getOrgaoConcedenteId())) {
				return calculateImportePeajeAGER(km, orgao, importe, rutaCantEixos, getRutaCantAsientos(ruta));
			}

			return BigDecimal.ZERO; // TODO throw an exception? como calcular quando é outro orgao concendente?
		}

		private static BigDecimal getIndicePeaje(ClaseServicio claseServicio,
				OrgaoConcedente orgao, List<ClasseIndicePeaje> lsClasseIndicePeaje) {

			for (ClasseIndicePeaje classeIndicePeaje : lsClasseIndicePeaje) {
				if (claseServicio.equals(classeIndicePeaje.getClasseServicio())) {
					return classeIndicePeaje.getIndicePeaje();
				}
			}
			return orgao.getIndicePeaje();
		}

		private static PeajeVO create(Ruta ruta, OrgaoConcedente orgao,
				Object[] obj, List<ClasseIndicePeaje> lsClasseIndicePeaje, boolean usaICMS, BigDecimal km) {

			PeajeVO pvo = new PeajeVO();

			pvo.rutaId = ruta.getRutaId();
			pvo.cantEixos = ruta.getCantEixos();
			pvo.cantAsientos = ruta.getCantAsientos();
			pvo.origenId = Integer.parseInt(obj[8].toString());
			pvo.destinoId = Integer.parseInt(obj[9].toString());
			pvo.orgaoId = orgao.getOrgaoConcedenteId();
			pvo.casetaPeajeId = Integer.parseInt(obj[20].toString());
			pvo.indicePeaje = getIndicePeaje(ruta.getClaseServicio(), orgao, lsClasseIndicePeaje);
			
			pvo.importePeaje = calculateImportePeaje(ruta, orgao, obj, pvo.indicePeaje, usaICMS, km);

			pvo.activo = 1;
			pvo.fecmodif = new Date();
			pvo.usuarioId = UsuarioLogado.getUsuarioLogado().getUsuarioId();

			return pvo;
		}

		private String toInsertQuery() {
			return new StringBuilder()
					.append("insert into peaje ( peaje_id, ruta_id, origen_id, destino_id, ")
					.append("  orgaoconcedente_id, casetapeaje_id, cantasientos, canteixos, ")
					.append("  importepeaje, indicepeaje, activo, fecmodif, usuario_id ) ")
					.append("values ( peaje_seq.nextval, ")
					.append(rutaId).append(", ")
					.append(origenId).append(", ")
					.append(destinoId).append(", ")
					.append(orgaoId).append(", ")
					.append(casetaPeajeId).append(", ")
					.append(cantAsientos).append(", ")
					.append(cantEixos).append(", ")
					.append(importePeaje).append(", ")
					.append(indicePeaje).append(", ")
					.append(activo).append(", ")
					.append("to_date('")
					.append(SDF.format(fecmodif))
					.append("', 'yyyy/mm/dd hh24:mi:ss'), ")
					.append(usuarioId).append(" ) ")
					.toString();
		}

		private String toUpdateExcepcionPeajeQuery() {
			return new StringBuilder()
					.append("update excepcion_peaje ")
					.append("set activo = 0 ")
					.append("where ruta_id = ").append(rutaId)
					.append("  and origen_id = ").append(origenId)
					.append("  and destino_id = ").append(origenId)
					.toString();
		}

		private String toUpdateExcepcionPeajeVigenciaQuery() {
			return new StringBuilder()
					.append("update excepcion_peaje_vigencia ")
					.append("set activo = 0 ")
					.append("where excepcionpeaje_id in ( ")
					.append("  select excepcionpeaje_id ")
					.append("  from excepcion_peaje ")
					.append("  where ruta_id = ").append(rutaId)
					.append("    and origen_id = ").append(origenId)
					.append("    and destino_id = ").append(origenId)
					.append(")")
					.toString();
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((activo == null) ? 0 : activo.hashCode());
			result = prime * result + ((cantAsientos == null) ? 0 : cantAsientos.hashCode());
			result = prime * result + ((cantEixos == null) ? 0 : cantEixos.hashCode());
			result = prime * result + ((casetaPeajeId == null) ? 0 : casetaPeajeId.hashCode());
			result = prime * result + ((destinoId == null) ? 0 : destinoId.hashCode());
			result = prime * result + ((importePeaje == null) ? 0 : importePeaje.hashCode());
			result = prime * result + ((indicePeaje == null) ? 0 : indicePeaje.hashCode());
			result = prime * result + ((orgaoId == null) ? 0 : orgaoId.hashCode());
			result = prime * result + ((origenId == null) ? 0 : origenId.hashCode());
			result = prime * result + ((rutaId == null) ? 0 : rutaId.hashCode());
			result = prime * result + ((usuarioId == null) ? 0 : usuarioId.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			PeajeVO other = (PeajeVO) obj;
			if (activo == null) {
				if (other.activo != null)
					return false;
			} else if (!activo.equals(other.activo))
				return false;
			if (cantAsientos == null) {
				if (other.cantAsientos != null)
					return false;
			} else if (!cantAsientos.equals(other.cantAsientos))
				return false;
			if (cantEixos == null) {
				if (other.cantEixos != null)
					return false;
			} else if (!cantEixos.equals(other.cantEixos))
				return false;
			if (casetaPeajeId == null) {
				if (other.casetaPeajeId != null)
					return false;
			} else if (!casetaPeajeId.equals(other.casetaPeajeId))
				return false;
			if (destinoId == null) {
				if (other.destinoId != null)
					return false;
			} else if (!destinoId.equals(other.destinoId))
				return false;
			if (importePeaje == null) {
				if (other.importePeaje != null)
					return false;
			} else if (!importePeaje.equals(other.importePeaje))
				return false;
			if (indicePeaje == null) {
				if (other.indicePeaje != null)
					return false;
			} else if (!indicePeaje.equals(other.indicePeaje))
				return false;
			if (orgaoId == null) {
				if (other.orgaoId != null)
					return false;
			} else if (!orgaoId.equals(other.orgaoId))
				return false;
			if (origenId == null) {
				if (other.origenId != null)
					return false;
			} else if (!origenId.equals(other.origenId))
				return false;
			if (rutaId == null) {
				if (other.rutaId != null)
					return false;
			} else if (!rutaId.equals(other.rutaId))
				return false;
			if (usuarioId == null) {
				if (other.usuarioId != null)
					return false;
			} else if (!usuarioId.equals(other.usuarioId))
				return false;
			return true;
		}
		
	}
	
}
