package fr.emse.gitlab.saref.jobs;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.jena.atlas.io.IndentedWriter;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.ReadWrite;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.engine.binding.Binding;
import org.apache.jena.sparql.engine.binding.BindingHashMap;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.vocabulary.RDF;

import fr.emse.ci.sparqlext.SPARQLExt;
import fr.emse.ci.sparqlext.engine.PlanFactory;
import fr.emse.ci.sparqlext.engine.RootPlan;
import fr.emse.ci.sparqlext.stream.LookUpRequest;
import fr.emse.ci.sparqlext.stream.SPARQLExtStreamManager;
import fr.emse.ci.sparqlext.utils.ContextUtils;
import fr.emse.ci.sparqlext.utils.VarUtils;
import fr.emse.gitlab.saref.Constants;
import fr.emse.gitlab.saref.utils.Languages;
import fr.emse.gitlab.saref.vocabs.EX;
import fr.emse.gitlab.saref.vocabs.RDFP;

public class GeneratePortal extends JobRunner {

	private static final String DOC_BASE = Constants.BASE + "documentation/";
	private static final String TERM_QUERY = "documentation/term/main.rqg";
	private static final Var VAR_TERM = VarUtils.allocVar("term");
	private final RootPlan planForTerm;

	private static final String ONTO_QUERY = "documentation/ontology/main.rqg";
	private static final Var VAR_VERSION_IRI = VarUtils.allocVar("versionIRI");
	private final RootPlan planForOntologyVersion;

	private static final String EXAMPLE_QUERY = "documentation/example/main.rqg";
	private static final Var VAR_EXAMPLE = VarUtils.allocVar("example");
	private final RootPlan planForExample;

	private final SPARQLExtStreamManager streamManager;

	public GeneratePortal(String testSuiteName, SPARQLExtStreamManager streamManager) throws IOException {
		super(testSuiteName);
		this.streamManager = streamManager;

		String query = IOUtils.toString(streamManager.open(new LookUpRequest(TERM_QUERY, SPARQLExt.MEDIA_TYPE)),
				StandardCharsets.UTF_8);
		planForTerm = PlanFactory.create(query, DOC_BASE);

		query = IOUtils.toString(streamManager.open(new LookUpRequest(ONTO_QUERY, SPARQLExt.MEDIA_TYPE)),
				StandardCharsets.UTF_8);
		planForOntologyVersion = PlanFactory.create(query, DOC_BASE);

		query = IOUtils.toString(streamManager.open(new LookUpRequest(TERM_QUERY, SPARQLExt.MEDIA_TYPE)),
				StandardCharsets.UTF_8);
//		planForExample = PlanFactory.create(query, DOC_BASE);
		planForExample = null;

	}

	public void doJob(Dataset dataset, File directory, boolean verbose, boolean ignoreExamples, boolean ignoreTerms) {
		File siteDir = new File(directory, Constants.SITE_DIR);
		dataset.begin(ReadWrite.READ);
		Model config = dataset.getNamedModel(Constants.CONFIG);

		File configFile = new File(siteDir, "config.ttl");
		try (FileOutputStream fos = new FileOutputStream(configFile)) {
			config.write(fos, "TTL");
		} catch (IOException ex) {
			logger.error("Unexpected exception while writing configuration file", ex);
		}
		
		for (ResIterator it = config.listResourcesWithProperty(RDF.type, EX.OntologyVersion); it.hasNext();) {
			Resource resource = it.next();// https://saref.etsi.org/core/master/
			String uri = resource.getURI();
			String ontoDirPath = uri.substring(Constants.BASE.length(), uri.length() - 1); // core/master
			try {
				File ontoDir = new File(siteDir, ontoDirPath);
				FileUtils.forceMkdir(ontoDir);
				String repoName = resource.getRequiredProperty(EX.repositoryName).getString(); // saref-core
				String ontologyFileName = repoName.equals("saref-core") ? "saref" : repoName; // saref
				Model model = dataset.getNamedModel(resource.getURI());
				for (Languages l : Languages.values()) {
					File file = new File(ontoDir, String.format("%s.%s", ontologyFileName, l.getExt()));
					try (FileOutputStream fos = new FileOutputStream(file)) {
						model.write(fos, l.getLang());
					}
				}
				File htmlFile = new File(ontoDir, String.format("%s.html", ontologyFileName));
				try (IndentedWriter writer = new IndentedWriter(new FileOutputStream(htmlFile));) {
					Context context = ContextUtils.build(writer).setBase(Constants.BASE)
							.setDebugTemplate(verbose).setInputDataset(dataset)
							.setStreamManager(streamManager).build();
					generateHTML(planForOntologyVersion, VAR_VERSION_IRI, resource, context);
				}
			} catch (Exception ex) {
				logger.error(String.format("Unexpected exception while writing static files for %s", resource), ex);
			}
			try {
				File diagramDir = new File(siteDir, ontoDirPath + File.separator + "diagrams");
				FileUtils.copyDirectory(new File(directory, "diagrams"), diagramDir);
			} catch (IOException ex) {
				logger.warn("Could not copy diagrams directory", ex);
			}
		}

		if(!ignoreTerms) {
			for (ResIterator it = config.listResourcesWithProperty(RDF.type, RDFP.Resource); it.hasNext();) {
				Resource resource = it.next();// https://saref.etsi.org/core/master/ -- https://saref.etsi.org/core/Command
				String uri = resource.getURI();
				try {
					String termPath = uri.substring(Constants.BASE.length()); // core/Command
					File termDir = new File(siteDir, termPath).getParentFile(); // core
					FileUtils.forceMkdir(termDir);
					String termName = resource.getRequiredProperty(EX.localName).getString(); // Command
					Model model = dataset.getNamedModel(resource.getURI());
					for (Languages l : Languages.values()) {
						File file = new File(termDir, String.format("%s.%s", termName, l.getExt()));
						try (FileOutputStream fos = new FileOutputStream(file)) {
							model.write(fos, l.getLang());
						}
					}
					File htmlFile = new File(termDir, String.format("%s.html", termName));
					try (IndentedWriter writer = new IndentedWriter(new FileOutputStream(htmlFile));) {
						Context context = ContextUtils.build(writer).setBase(Constants.BASE)
								.setDebugTemplate(verbose).setInputDataset(dataset)
								.setStreamManager(streamManager).build();
						generateHTML(planForTerm, VAR_TERM, resource, context);
					}
				} catch (Exception ex) {
					logger.error(String.format("Unexpected exception while writing static files for %s", resource), ex);
				}
			}
		}

		if(!ignoreExamples) {
			for (ResIterator it = config.listResourcesWithProperty(RDF.type, EX.Example); it.hasNext();) {
				Resource resource = it.next();// https://saref.etsi.org/core/master/example/doorswitch
				String uri = resource.getURI();
				try {
					String exPath = uri.substring(Constants.BASE.length()); // core/master/example/doorswitch
					File exDir = new File(siteDir, exPath).getParentFile(); // core/master/example
					FileUtils.forceMkdir(exDir);
					String exName = resource.getRequiredProperty(EX.localName).getString(); // doorswitch
					Model model = dataset.getNamedModel(resource.getURI());
					for (Languages l : Languages.values()) {
						File file = new File(exDir, String.format("%s.%s", exName, l.getExt()));
						try (FileOutputStream fos = new FileOutputStream(file)) {
							model.write(fos, l.getLang());
						}
					}
	//				File htmlFile = new File(exDir, String.format("%s.html", exName));
	//				try (IndentedWriter writer = new IndentedWriter(new FileOutputStream(htmlFile));) {
	//					Context context = ContextUtils.build(writer).setBase(Constants.BASE)
	//							.setDebugTemplate(!Constants.PRODUCTION).setInputDataset(dataset)
	//							.setStreamManager(streamManager).build();
	//					generateHTML(planForExample, VAR_EXAMPLE, resource, context);
	//				}
				} catch (Exception ex) {
					logger.error(String.format("Unexpected exception while writing static files for %s", resource), ex);
				}
			}
		}
		
		
		dataset.end();
	}

	private void generateHTML(RootPlan plan, Var var, Resource resource, Context context) {
		BindingHashMap binding = new BindingHashMap();
		binding.add(var, resource.asNode());
		List<Binding> bindings = new ArrayList<>();
		bindings.add(binding);
		plan.execTemplateStream(bindings, context);
	}

}
