/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rjconsultores.ventaboletos.dao.hibernate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.FetchMode;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Repository;

import com.rjconsultores.ventaboletos.dao.RutaSecuenciaDAO;
import com.rjconsultores.ventaboletos.entidad.EsquemaCorrida;
import com.rjconsultores.ventaboletos.entidad.Parada;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.entidad.RutaSecuencia;
import com.rjconsultores.ventaboletos.utilerias.UsuarioLogado;

@SuppressWarnings("unchecked")
@Repository("rutaSecuenciaDAO")
public class RutaSecuenciaHibernateDAO extends GenericHibernateDAO<RutaSecuencia, Integer>
		implements RutaSecuenciaDAO {

	@Autowired
	public RutaSecuenciaHibernateDAO(@Qualifier("sessionFactory") SessionFactory factory) {
		setSessionFactory(factory);
	}

	public List<RutaSecuencia> buscarSecuenciaOrdenado(Ruta ruta) {
		Criteria c = getSession().createCriteria(getPersistentClass());

		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.add(Restrictions.eq("ruta", ruta));
		c.setFetchMode("lsRutaCase", FetchMode.JOIN);

		c.addOrder(Order.asc("numsecuencia"));

		return c.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
	}

	public int borrarSecuencias(Ruta ruta) {
		Query query = this.getSession().createQuery("UPDATE RutaSecuencia SET activo = 0, fecmodif =  current_timestamp(), usuarioId= :usuario where ruta = :ruta");
		query.setParameter("usuario", UsuarioLogado.getUsuarioLogado().getUsuarioId());
		query.setParameter("ruta", ruta);

		return query.executeUpdate();
	}

	@Override
	public boolean paradaSecuenciaValida(Ruta ruta, Parada origen, Parada destino) {
		Criteria cOrigen = makeCriteria();
		cOrigen.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		cOrigen.add(Restrictions.eq("ruta", ruta));
		cOrigen.setProjection(Projections.property("numsecuencia"));
		
		Criteria cOrigenTramo = cOrigen.createCriteria("tramo");
		cOrigenTramo.add(Restrictions.eq("origem", origen));
		

		Short numSecOrigen = (Short) cOrigen.uniqueResult();

		Criteria cDestino = makeCriteria();
		cDestino.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		cDestino.add(Restrictions.eq("ruta", ruta));
		cDestino.setProjection(Projections.property("numsecuencia"));
		
		Criteria cDestinoTramo = cDestino.createCriteria("tramo");
		cDestinoTramo.add(Restrictions.eq("origem", destino));
		

		Short numSecDestino = (Short) cDestino.uniqueResult();
		boolean destinoFinal = false;
		// destino final da ruta
		if (numSecDestino == null) {
			cDestino = makeCriteria();
			cDestino.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
			cDestino.add(Restrictions.eq("ruta", ruta));
			cDestino.setProjection(Projections.property("numsecuencia"));
			
			cDestinoTramo = cDestino.createCriteria("tramo");
			cDestinoTramo.add(Restrictions.eq("destino", destino));
			

			numSecDestino = (Short) cDestino.uniqueResult();
			
			destinoFinal = true;//quando é o destino final (ultima parada da secuencia), o numero da secOrigem pode ser igual ao secDestino
		}
		
		if ((numSecOrigen != null) && (numSecDestino != null)) {
			return  (destinoFinal)?(numSecOrigen.compareTo(numSecDestino) <= 0):(numSecOrigen.compareTo(numSecDestino) < 0);
		}

		return false;
	}

	public List<Short> obtenerNumRutaSecuenciaPorCorridaOrigemDestino(EsquemaCorrida esquemaCorrida, Parada origem, Parada destino) {
		
		String queryString = "SELECT rs.numsecuencia "
				+ "FROM EsquemaCorrida ec "
				+ "JOIN ec.ruta r "
				+ "JOIN r.rutaSecuenciaList rs "
				+ "JOIN rs.tramo t "
				+ "WHERE (t.origem = :origem OR t.destino = :destino) "
				+ "AND ec = :esquemaCorrida";

		Query query = getSession().createQuery(queryString);
		query.setParameter("esquemaCorrida", esquemaCorrida);
		query.setParameter("origem", origem);
		query.setParameter("destino", destino);
		
		return query.list();
		
	}
	
	public List<Short> obtenerNumRutaSecuenciaPorCorridaOrigemDestinoId(EsquemaCorrida esquemaCorrida, Integer origemId, Integer destinoId) {
		
		StringBuilder queryString = new StringBuilder("");
		
		queryString.append("SELECT rs.numsecuencia  ");
		queryString.append("	FROM EsquemaCorrida ec  ");
		queryString.append("		JOIN ec.ruta r ");
		queryString.append("		JOIN r.rutaSecuenciaList rs ");
		queryString.append("		JOIN rs.tramo t ");
		queryString.append("	WHERE  ");
		queryString.append(" 		(t.origem.paradaId = :origem OR t.destino.paradaId = :destino) ");
		queryString.append(" 		AND ec = :esquemaCorrida");
		queryString.append(" 		and rs.activo = 1 ");
		
		Query query = getSession().createQuery(queryString.toString());
		query.setParameter("esquemaCorrida", esquemaCorrida);
		query.setParameter("origem", origemId);
		query.setParameter("destino", destinoId);
		
		return query.list();
		
	}
	
	public List<Integer> obtenerNumRutaSecuenciaOcupadaPorCorridaAsiento(EsquemaCorrida esquemaCorrida, String asiento) {
		
		String queryString = "SELECT ea.esquemaasientoId, rs.numsecuencia, ea.indvendible, "
				+ "(SELECT MAX(rs2.numsecuencia) FROM RutaSecuencia rs2 WHERE rs2.ruta = r) "
				+ "FROM EsquemaAsiento ea "
				+ "JOIN ea.esquemaCorrida ec "
				+ "JOIN ec.ruta r "
				+ "JOIN r.rutaSecuenciaList rs "
				+ "JOIN rs.tramo t "
				+ "WHERE ea.numasiento = :asiento "
				+ "AND (t.origem = ea.parada "
				+ "OR t.destino = ea.destino) "
				+ "AND ec = :esquemaCorrida "
				+ "AND ea.activo = 1 "
				+ "ORDER BY rs.numsecuencia";
		
		Query query = getSession().createQuery(queryString);
		query.setParameter("asiento", asiento);
		query.setParameter("esquemaCorrida", esquemaCorrida);
		
		List<Integer> numSecuenciaOcupada = new ArrayList<Integer>();
		List<Object[]> resultList = query.list();
		
		HashMap<Integer, List<Object[]>> mapObjectsByEsquema = new HashMap<Integer, List<Object[]>>();
		for (Object[] result : resultList) {
			Integer esquemaasientoId = (Integer) result[0];
			if (mapObjectsByEsquema.containsKey(esquemaasientoId)) {
				mapObjectsByEsquema.get(esquemaasientoId).add(result);
			} else {
				List<Object[]> numSecuenciaOcupadaList = new ArrayList<Object[]>();
				numSecuenciaOcupadaList.add(result);
				mapObjectsByEsquema.put(esquemaasientoId, numSecuenciaOcupadaList);
			}
		}
		
		for (Integer esquemaasientoId : mapObjectsByEsquema.keySet()) {
			
			List<Object[]> listObjects = mapObjectsByEsquema.get(esquemaasientoId);
			Short numsecuenciaOrigem = (Short) listObjects.get(0)[1];
			Boolean indvendible = (Boolean) listObjects.get(0)[2];
			Short ultimoNumsecuencia = (Short) listObjects.get(0)[3];
			Short numsecuenciaDestino = numsecuenciaOrigem;
			if (Boolean.TRUE.equals(indvendible)) {
				numsecuenciaDestino = ultimoNumsecuencia;
			} else if (listObjects.size() > 1) {
				numsecuenciaDestino = (Short) listObjects.get(1)[1];
			}
			
			Integer min = Math.min(numsecuenciaOrigem, numsecuenciaDestino);
			Integer max = Math.max(numsecuenciaOrigem, numsecuenciaDestino);
			for (int j = min; j <= max; j++) {
				numSecuenciaOcupada.add(j);
			}
			
		}
		
		return numSecuenciaOcupada;
		
	}
	
}
