package com.rjconsultores.ventaboletos.web.gui.controladores.relatorios.sgti;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.zkoss.zkplus.spring.SpringUtil;

import com.rjconsultores.ventaboletos.entidad.Empresa;
import com.rjconsultores.ventaboletos.entidad.OrgaoEmpParam;
import com.rjconsultores.ventaboletos.entidad.Ruta;
import com.rjconsultores.ventaboletos.service.OrgaoEmpParamService;

public class GerarXMLExportacaoSGTI {

	private static final String SENTIDO_IDA_CORRIDA = "1";

	private static Logger log = LogManager.getLogger(GerarXMLExportacaoSGTI.class);
	private RelatorioExportacaoSGTIController controller;
	private ExecutarExportacaoSGTI exportador;
	private ResultSet dadosTrecho;
	private ResultSet dadosReceita;
	private ResultSet dadosViagemExtra;
	
	@Autowired
	private OrgaoEmpParamService orgaoEmpParamService;
	
	public GerarXMLExportacaoSGTI(RelatorioExportacaoSGTIController controller, 
			ExecutarExportacaoSGTI exportador) {
		this.controller = controller;
		this.exportador = exportador;
		orgaoEmpParamService = (OrgaoEmpParamService) SpringUtil.getBean("orgaoEmpParamService");
	}
	
	public File gerar() throws IOException {
		File fileXML = File.createTempFile("Exportacao_SGTI", ".xml");
		try {
			Document doc = inicializaDOM();

			Map<String, String> map = new HashMap<String, String>();
			
			inicializaDadosExportacao();

			Element tagEntrega = gerarTag(doc, "Entrega", getAtributosEntrega(map));
			for (Ruta ruta : getLinhasSelecionadas()) {
				Element tagQdmp = gerarTag(doc, "Qdmp", getAtributosLinha(map, ruta));
				Element tagPeriodo = gerarTag(doc, "Periodo", getAtributosPeriodo(map, ruta));
				tagQdmp.appendChild(tagPeriodo);
				gerarTagsPeriodo(doc, map, tagPeriodo, ruta);
				tagEntrega.appendChild(tagQdmp);
			}

			doc.appendChild(tagEntrega);

			transformaDOMToXML(doc, fileXML);

		} catch (Exception e) {
			log.error("Ocorreu um erro ao gerar o XML. "+e.getMessage(), e);
		}
		return fileXML;
	}

	private void inicializaDadosExportacao() {
		try {
			dadosTrecho = exportador.getDadosTrecho();
		} catch (SQLException e) {
			log.error("Ocorreu um erro ao consultar os trechos. "+e.getMessage(), e);
		}
		
		try {
			dadosReceita = exportador.getDadosReceita();
		} catch (SQLException e) {
			log.error("Ocorreu um erro ao obter os dados da receita. "+e.getMessage(), e);
		}
		
		try {
			dadosViagemExtra = exportador.getDadosViagemExtra();
		} catch (SQLException e) {
			log.error("Ocorreu um erro ao obter os dados da viagem extra. "+e.getMessage(), e);
		}
	}

	private void gerarTagsPeriodo(Document doc, Map<String, String> map, Element tagPeriodo, Ruta ruta) throws SQLException {
		dadosTrecho.beforeFirst();
		while (dadosTrecho.next()) {
			if (ruta.getRutaId() == dadosTrecho.getInt("ruta_id")) {
				tagPeriodo.appendChild(gerarTag(doc, "Trecho", getAtributosTrecho(map, dadosTrecho)));
			}
		}
		//Verificar o que é uma paralisação
		//tagPeriodo.appendChild(gerarTag(doc, "Paralisacao", getAtributosParalisacaoOuViagemExtra(map)));
		dadosViagemExtra.beforeFirst();
		while (dadosViagemExtra.next()) {
			if (ruta.getRutaId() == dadosViagemExtra.getInt("ruta_id")) {
				tagPeriodo.appendChild(gerarTag(doc, "ViagemExtra", getAtributosParalisacaoOuViagemExtra(map, dadosViagemExtra)));
			}
		}
	}

	private Document inicializaDOM() throws ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = dbf.newDocumentBuilder();
		return docBuilder.newDocument();
	}

	private void transformaDOMToXML(Document doc, File fileXML) throws FileNotFoundException, TransformerFactoryConfigurationError, TransformerConfigurationException, TransformerException {
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(new FileOutputStream(fileXML));

		TransformerFactory transFactory = TransformerFactory.newInstance();
		Transformer transformer = transFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		transformer.transform(source, result);
	}
	
	private Element gerarTag(Document doc, String tagName, Map<String, String> atributos) {
		Element tag = doc.createElement(tagName);
		for (Entry<String, String> atributo : atributos.entrySet()) {
			tag.setAttribute(atributo.getKey(), atributo.getValue());
		}
		return tag;
	}

	private Map<String, String> getAtributosEntrega(Map<String, String> map) {
		map.clear();
		map.put("codigoDelegatario", obtemCodigoDelegatario());
		map.put("ano", String.valueOf(getValueCalendar(Calendar.YEAR, controller.getDataInicial().getValue())));
		map.put("mes", String.valueOf(getValueCalendar(Calendar.MONTH, controller.getDataInicial().getValue()) + 1));
		return map;
	}

	private String obtemCodigoDelegatario() {
		OrgaoEmpParam orgaoEmpParam = orgaoEmpParamService.obterPorOrgaoEmpresa(
				RelatorioExportacaoSGTIController.ORGAO_CONCEDENTE_DER_MG, 
				((Empresa)controller.getCbEmpresa().getSelectedItem().getValue()).getEmpresaId());
		return orgaoEmpParam != null ? orgaoEmpParam.getCodEmpresaPorOrgao() : "";
	}

	private Map<String, String> getAtributosLinha(Map<String, String> map, Ruta ruta) {
		map.clear();
		map.put("linha", ruta.getPrefixo());
		return map;
	}

	private Map<String, String> getAtributosPeriodo(Map<String, String> map, Ruta ruta) throws SQLException {
		map.clear();
		map.put("diaInicio", String.valueOf(getValueCalendar(Calendar.DAY_OF_MONTH, controller.getDataInicial().getValue())));
		map.put("diaFim", String.valueOf(getValueCalendar(Calendar.DAY_OF_MONTH, controller.getDataFinal().getValue())));
		BigDecimal valorReceita = BigDecimal.ZERO;
		dadosReceita.beforeFirst();
		while (dadosReceita.next()) {
			if (ruta.getRutaId() == dadosReceita.getInt("ruta_id")) {
				valorReceita = dadosReceita.getBigDecimal("receita");
				break;
			}
		}
		map.put("receita", valorReceita.toString());
		return map;
	}

	private Map<String, String> getAtributosTrecho(Map<String, String> map, ResultSet resultSet) throws SQLException {
		map.clear();
		map.put("pontoInicial", resultSet.getString("cod_origem"));
		map.put("pontoFinal", resultSet.getString("cod_destino"));
		map.put("quantidadeIda", isSentidoIdaCorrida(resultSet.getString("indsentidoida")) ? resultSet.getString("qtde") : "0");
		map.put("quantidadeVolta", isSentidoIdaCorrida(resultSet.getString("indsentidoida")) ? "0" : resultSet.getString("qtde"));
		return map;
	}

	private Map<String, String> getAtributosParalisacaoOuViagemExtra(Map<String, String> map, ResultSet resultSet) throws SQLException {
		map.clear();
		map.put("dia", resultSet.getString("dia"));
		map.put("horario", resultSet.getString("horario"));
		map.put("trajeto", isSentidoIdaCorrida(resultSet.getString("indsentidoida")) ? "I" : "V");
		return map;
	}
	
	@SuppressWarnings("unchecked")
	private List<Ruta> getLinhasSelecionadas() {
		return (List<Ruta>) (Object)Arrays.asList(controller.getLinhaSelList().getData());
	}
	
	private int getValueCalendar(final int valueCalendar, Date data) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(data);
		return calendar.get(valueCalendar);
	}
	
	private boolean isSentidoIdaCorrida(String sentidoCorrida) {
		return SENTIDO_IDA_CORRIDA.equals(sentidoCorrida);
	}
}
