package com.rjconsultores.ventaboletos.dao.hibernate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Order;
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.ConexionDAO;
import com.rjconsultores.ventaboletos.entidad.Conexion;
import com.rjconsultores.ventaboletos.entidad.ConexionCtrlTemp;
import com.rjconsultores.ventaboletos.entidad.ConexionTemp;
import com.rjconsultores.ventaboletos.vo.conexion.ConexionVO;

@Repository("conexionDAO")
public class ConexionHibernateDAO extends GenericHibernateDAO<Conexion, Integer>
		implements ConexionDAO {

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

	@Override
	public List<ConexionVO> buscarConexionesValidas() {

		StringBuilder sb = new StringBuilder();
		sb.append("SELECT DISTINCT cct.conexionctrl_id AS conexionCtrlId, ");
		sb.append("                po1.descparada      AS conOrigen, ");
		sb.append("                pd1.descparada      AS conDestino, ");
		sb.append("                c.numgrupo          AS grupo, ");
		sb.append("                po2.descparada      AS tramoOrigen, ");
		sb.append("                pd2.descparada      AS tramoDestino, ");
		sb.append("                c.numsecuencia      AS numSecuencia ");
		sb.append("FROM   conexion c ");
		sb.append("       inner join conexion_conf cf ");
		sb.append("               ON ( cf.conexionctrl_id = c.conexionctrl_id ");
		sb.append("                    AND cf.activo = 1 ) ");
		sb.append("       inner join conexion_ctrl cct ");
		sb.append("               ON ( cct.conexionctrl_id = c.conexionctrl_id ");
		sb.append("                    AND cct.activo = 1 ) ");
		sb.append("       inner join parada po1 ");
		sb.append("               ON ( po1.parada_id = cct.origen_id ) ");
		sb.append("       inner join parada pd1 ");
		sb.append("               ON ( pd1.parada_id = cct.destino_id ) ");
		sb.append("       inner join parada po2 ");
		sb.append("               ON ( po2.parada_id = c.origen_id ) ");
		sb.append("       inner join parada pd2 ");
		sb.append("               ON ( pd2.parada_id = c.destino_id ) ");
		sb.append("WHERE  cct.activo = 1 and c.activo = 1 ");
		sb.append("ORDER  BY po1.descparada, ");
		sb.append("          pd1.descparada, ");
		sb.append("          cct.conexionctrl_id, ");
		sb.append("          c.numgrupo, ");
		sb.append("          c.numsecuencia, ");
		sb.append("          po2.descparada, ");
		sb.append("          pd2.descparada ");

		Query query = getSession().createSQLQuery(sb.toString());
		List<ConexionVO> conexionVOs = new ArrayList<ConexionVO>();

		for (Object o : query.list()) {
			Object[] oc = (Object[]) o;

			ConexionVO conexionVO = new ConexionVO();
			conexionVO.setConexionCtrlId(((BigDecimal) oc[0]).longValue());
			conexionVO.setConOrigen((String) oc[1]);
			conexionVO.setConDestino((String) oc[2]);
			conexionVO.setGrupo(((BigDecimal) oc[3]).intValue());
			conexionVO.setTramoOrigen((String) oc[4]);
			conexionVO.setTramoDestino((String) oc[5]);

			conexionVO.setBuscaAgruapada(false);

			conexionVOs.add(conexionVO);
		}

		return conexionVOs;
	}

	@Override
	public List<ConexionVO> buscarConexiones(boolean isBuscaAgrupada, Integer origenId, Integer destinoId) {

		String queryStr = getQueryConexiones(origenId, destinoId);

		Query query = getSession().createSQLQuery(queryStr);
		List<Object[]> list = query.list();

		List<ConexionVO> vos = new ArrayList<ConexionVO>();
		for (Object[] o : list) {
			ConexionVO vo = new ConexionVO();
			vo.setGrupo(((BigDecimal) o[0]).intValue());
			vo.setConexionCtrlId(((BigDecimal) o[1]).longValue());
			vo.setConOrigen((String) o[2]);
			vo.setConDestino((String) o[3]);
			vo.setTramoOrigen((String) o[4]);
			vo.setTramoDestino((String) o[5]);
			vo.setBuscaAgruapada(isBuscaAgrupada);

			vos.add(vo);
		}

		if (isBuscaAgrupada) {
			return agruparConexiones(vos);
		}

		return vos;
	}

	private List<ConexionVO> agruparConexiones(List<ConexionVO> conexiones) {

		LinkedHashMap<String, List<ConexionVO>> map = new LinkedHashMap<String, List<ConexionVO>>();

		List<ConexionVO> agrpVo = new ArrayList<ConexionVO>();

		String grupoTrecho = null;
		Integer grupo = null;
		for (ConexionVO vo : conexiones) {

			if (grupo == null)
				grupo = vo.getGrupo();

			if (!grupo.equals(vo.getGrupo())) {
				if (!map.containsKey(grupoTrecho)) {
					map.put(grupoTrecho, agrpVo);
				}

				grupo = vo.getGrupo();
				grupoTrecho = null;
				agrpVo = new ArrayList<ConexionVO>();
			}

			if (grupo.equals(vo.getGrupo())) {

				if (grupoTrecho == null) {
					grupoTrecho = vo.getTramoOrigen() + ";" + vo.getTramoDestino() + ";";
				} else {
					grupoTrecho += vo.getTramoOrigen() + ";" + vo.getTramoDestino() + ";";
				}

				agrpVo.add(vo);
			}

		}

		List<ConexionVO> agrupados = new ArrayList<ConexionVO>();
		for (Map.Entry<String, List<ConexionVO>> entry : map.entrySet()) {
			agrupados.addAll(entry.getValue());
		}

		return agrupados;
	}

	private String getQueryConexiones(Integer origenId, Integer destinoId) {

		StringBuilder sb = new StringBuilder();
		sb.append(" select ");
		sb.append(" c.numgrupo as grupo, ");
		sb.append("  cc.conexionctrl_id as conexionCtrlId, occ.descparada as conOrigen, dcc.descparada as conDestino, ");
		sb.append("  oc.descparada as tramoOrigen, dc.descparada as tramoDestino, c.numsecuencia as  numsecuencia ");
		sb.append(" from   conexion_ctrl cc ");
		sb.append("       inner join conexion_conf conf ");
		sb.append("               on conf.conexionctrl_id = cc.conexionctrl_id, ");
		sb.append("       conexion c, ");
		sb.append("       parada occ, ");
		sb.append("       parada dcc, ");
		sb.append("       parada oc, ");
		sb.append("       parada dc ");
		sb.append("       where cc.activo = 1 and c.activo = 1 and conf.activo = 1");
		sb.append("       and cc.conexionctrl_id = c.conexionctrl_id ");
		sb.append("       and occ.parada_id = cc.origen_id ");
		sb.append(" 	  and conf.grupo = c.numgrupo ");
		sb.append("       and dcc.parada_id = cc.destino_id ");
		sb.append("       and oc.parada_id = c.origen_id ");
		sb.append("       and dc.parada_id = c.destino_id ");

		if (origenId != null) {
			sb.append(" and cc.origen_id = ").append(origenId);
		}

		if (destinoId != null) {
			sb.append(" and cc.destino_id = ").append(destinoId);
		}

		sb.append(" order  by conOrigen, conDestino, conexionCtrlId, grupo, numsecuencia, tramoOrigen, tramoDestino ");
		return sb.toString();
	}

	@Override
	public void generarConexiones(Integer usuarioId) {
		this.getSession().createQuery("DELETE ConexionTemp").executeUpdate();
		this.getSession().createQuery("DELETE ConexionCtrlTemp").executeUpdate();

		Query queryParadas = this.getSession().createQuery("select distinct t.origem.paradaId,t.destino.paradaId from Tramo t where t.activo = 1 order by t.origem.paradaId ");
		List<Object[]> lsParadas = queryParadas.list();

		StringBuilder sb = new StringBuilder("");
		sb.append("SELECT ");
		sb.append("	t.origem.paradaId, ");
		sb.append("	t.destino.paradaId, ");
		sb.append("	t2.origem.paradaId, ");
		sb.append("	t2.destino.paradaId, ");
		sb.append("    MAX(t.kmReal), ");
		sb.append("    MAX(tcs.tiemporecorrido), ");
		sb.append("    MAX(t2.kmReal), ");
		sb.append("    MAX(tcs2.tiemporecorrido) ");
		sb.append(" ");
		sb.append("FROM ");
		sb.append("    Tramo t, ");
		sb.append("    Tramo t2, ");
		sb.append("    TramoServicio tcs, ");
		sb.append("    TramoServicio tcs2 ");
		sb.append("     ");
		sb.append("where ");
		sb.append("	t.origem.paradaId = :origen ");
		sb.append("	and t.activo=1 ");
		sb.append("	 ");
		sb.append("	and t2.destino.paradaId = :destino ");
		sb.append("	and t2.activo=1 ");
		sb.append("	and t2.origem= t.destino ");
		sb.append("	 ");
		sb.append("	and tcs.tramo=t ");
		sb.append("	and tcs.activo=1 ");
		sb.append("  ");
		sb.append("	and tcs2.tramo=t2 ");
		sb.append("	and tcs2.activo=1 ");
		sb.append("  ");
		sb.append("group by ");
		sb.append("    t.origem.paradaId, ");
		sb.append("    t.destino.paradaId, ");
		sb.append("    t2.origem.paradaId, ");
		sb.append("    t2.destino.paradaId ");

		Map<String, Long> mapConexionCtrlId = new HashMap<String, Long>();
		for (Object[] obj : lsParadas) {
			int grupo = 0;
			Integer origenId = (Integer) obj[0];
			Integer destinoId = (Integer) obj[1];

			Query query = getSession().createQuery(sb.toString());
			query.setParameter("origen", origenId);
			query.setParameter("destino", destinoId);

			List<Object[]> list = query.list();

			for (Object[] objConexion : list) {
				Integer origen1 = (Integer) objConexion[0];
				Integer destino1 = (Integer) objConexion[1];
				Integer origen2 = (Integer) objConexion[2];
				Integer destino2 = (Integer) objConexion[3];

				String claveConexionCtrl = origenId + "-" + destinoId;

				Long conexionCtrlId = mapConexionCtrlId.get(claveConexionCtrl);

				if (conexionCtrlId == null) {
					ConexionCtrlTemp c = new ConexionCtrlTemp();
					c.setActivo(true);
					c.setOrigenId(origen1);
					c.setDestinoId(destino2);
					c.setFecmodif(new java.util.Date());
					c.setUsuarioId(usuarioId);

					conexionCtrlId = (Long) this.getSession().save(c);
					mapConexionCtrlId.put(claveConexionCtrl, conexionCtrlId);
				}
				grupo++;

				ConexionTemp cTemp = new ConexionTemp();
				cTemp.setActivo(true);
				cTemp.setConexionctrlId(conexionCtrlId);
				cTemp.setOrigenId(origen1);
				cTemp.setDestinoId(destino1);
				cTemp.setNumgrupo(grupo);
				cTemp.setNumsecuencia((short) 1);
				cTemp.setUsuarioId(usuarioId);
				cTemp.setFecmodif(new java.util.Date());

				ConexionTemp cTemp2 = new ConexionTemp();
				cTemp2.setActivo(true);
				cTemp2.setConexionctrlId(conexionCtrlId);
				cTemp2.setOrigenId(origen2);
				cTemp2.setDestinoId(destino2);
				cTemp2.setNumgrupo(grupo);
				cTemp2.setNumsecuencia((short) 2);
				cTemp2.setUsuarioId(usuarioId);
				cTemp2.setFecmodif(new java.util.Date());

				getSession().save(cTemp);
				getSession().save(cTemp2);
			}
		}

		// deletando, exceto conexoes que foram criadas manualmente:
		this.getSession().createQuery("DELETE Conexion c WHERE c.conexionctrlId NOT IN (SELECT cc.conexionCtrl.conexionctrlId FROM ConexionConf cc)").executeUpdate();
		this.getSession().createQuery("DELETE ConexionCtrl cct WHERE cct.conexionctrlId NOT IN (SELECT cc.conexionCtrl.conexionctrlId FROM ConexionConf cc)").executeUpdate();

		sb = new StringBuilder("");
		sb.append(" INSERT INTO ConexionCtrl (conexionctrlId,fecmodif,activo,usuarioId,origenId,destinoId) ");
		sb.append(" SELECT cct.conexionctrlId, ");
		sb.append("   cct.fecmodif, ");
		sb.append("   cct.activo, ");
		sb.append("   cct.usuarioId, ");
		sb.append("   cct.origenId, ");
		sb.append("   cct.destinoId ");
		sb.append(" FROM ConexionCtrlTemp cct ");
		sb.append(" WHERE cct.origenId || '_' || cct.destinoId NOT IN ");
		sb.append(" (   SELECT cc.origenId || '_' || cc.destinoId ");
		sb.append("     FROM ConexionCtrl cc ");
		sb.append("     WHERE cc.activo = 1) ");
		this.getSession().createQuery(sb.toString()).executeUpdate();

		sb = new StringBuilder("");
		sb.append(" INSERT INTO Conexion (conexionId,numgrupo,numsecuencia,activo,fecmodif,usuarioId,origenId,destinoId,conexionctrlId) ");
		sb.append(" SELECT ct.conexionId, ");
		sb.append("   ct.numgrupo, ");
		sb.append("   ct.numsecuencia, ");
		sb.append("   ct.activo, ");
		sb.append("   ct.fecmodif, ");
		sb.append("   ct.usuarioId, ");
		sb.append("   ct.origenId, ");
		sb.append("   ct.destinoId, ");
		sb.append("   ct.conexionctrlId ");
		sb.append(" FROM ConexionTemp ct ");
		sb.append(" WHERE ct.origenId || '_' || ct.destinoId NOT IN ");
		sb.append(" (   SELECT c.origenId || '_' || c.destinoId ");
		sb.append("     FROM Conexion c ");
		sb.append(" 	WHERE c.activo = 1) ");
		this.getSession().createQuery(sb.toString()).executeUpdate();
	}

	@Override
	public List<Conexion> buscarPorConexionCtrl(Long conexionCtrlId) {
		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq("conexionctrlId", conexionCtrlId));
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		c.addOrder(Order.asc("numgrupo"));
		c.addOrder(Order.asc("numsecuencia"));

		return c.list();
	}

	@Override
	public Conexion buscarPorConexionRutaTramoCtrl(Integer conexionRutaTramoId) {

		Criteria c = getSession().createCriteria(getPersistentClass());
		c.add(Restrictions.eq("conexionRutaTramoId", conexionRutaTramoId));
		c.add(Restrictions.eq(ACTIVO, Boolean.TRUE));
		return (Conexion) c.uniqueResult();
	}
}
