package fr.emse.gitlab.saref.jobs;

import java.io.StringWriter;
import java.util.Collections;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.jena.query.Dataset;
import org.semanticweb.HermiT.ReasonerFactory;
import org.semanticweb.owl.explanation.api.Explanation;
import org.semanticweb.owl.explanation.api.ExplanationGenerator;
import org.semanticweb.owl.explanation.api.ExplanationGeneratorFactory;
import org.semanticweb.owl.explanation.impl.blackbox.Configuration;
import org.semanticweb.owl.explanation.impl.blackbox.ContractionStrategy;
import org.semanticweb.owl.explanation.impl.blackbox.DivideAndConquerContractionStrategy;
import org.semanticweb.owl.explanation.impl.blackbox.EntailmentCheckerFactory;
import org.semanticweb.owl.explanation.impl.blackbox.ExpansionStrategy;
import org.semanticweb.owl.explanation.impl.blackbox.InitialEntailmentCheckStrategy;
import org.semanticweb.owl.explanation.impl.blackbox.StructuralTypePriorityExpansionStrategy;
import org.semanticweb.owl.explanation.impl.blackbox.checker.BlackBoxExplanationGeneratorFactory;
import org.semanticweb.owl.explanation.impl.blackbox.checker.InconsistentOntologyExplanationGeneratorFactory;
import org.semanticweb.owl.explanation.impl.blackbox.checker.SatisfiabilityEntailmentCheckerFactory;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.OWLParserFactory;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.profiles.OWL2DLProfile;
import org.semanticweb.owlapi.profiles.OWLProfileReport;
import org.semanticweb.owlapi.profiles.OWLProfileViolation;
import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration;
import org.semanticweb.owlapi.util.PriorityCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.emse.gitlab.saref.SAREFPipelineException;
import fr.emse.gitlab.saref.utils.JenaModelOntologyParserFactory;

public class CheckOWLProfile extends JobRunner {

	static final Logger LOG = LoggerFactory.getLogger(CheckOWLProfile.class);

	public CheckOWLProfile(String testSuiteName) {
		super(testSuiteName);
	}

	public void doJob(Dataset dataset, String ontologyName) throws SAREFPipelineException {
		LOG.debug("starting " + ontologyName);
		final OWLDataFactory dataFactory = OWLManager.getOWLDataFactory();
		final OWLOntologyManager ontologyManager = OWLManager.createOWLOntologyManager();

		OWLParserFactory jenaModelParserFactory = new JenaModelOntologyParserFactory(dataset);
		PriorityCollection<OWLParserFactory> ontologyParsers = ontologyManager.getOntologyParsers();
		ontologyParsers.add(jenaModelParserFactory);
//		ontologyManager.setOntologyParsers(ontologyParsers);

		final Supplier<OWLOntologyManager> m = () -> ontologyManager;
		final OWLOntology ontology;
		try {
			ontology = ontologyManager.loadOntologyFromOntologyDocument(IRI.create(ontologyName));
		} catch (OWLOntologyCreationException ex) {
			logger.error("Error while loading the ontology in OWLAPI", ex);
			throw new SAREFPipelineException("Error while loading the ontology in OWLAPI", ex);
		}
		final OWLProfileReport report = new OWL2DLProfile().checkOntology(ontology);
		for (OWLProfileViolation v : report.getViolations()) {
			logger.warn(v.toString());
		}
		final ReasonerFactory reasonerFactory = new ReasonerFactory();
		final InconsistentOntologyExplanationGeneratorFactory inconsistentOntologyExplanationFeneratorFactory = new InconsistentOntologyExplanationGeneratorFactory(
				reasonerFactory, ontologyManager.getOWLDataFactory(), m, 10000);
		final ExplanationGenerator<OWLAxiom> gen = inconsistentOntologyExplanationFeneratorFactory
				.createExplanationGenerator(ontology);
		final OWLAxiom inc = dataFactory.getOWLSubClassOfAxiom(dataFactory.getOWLThing(), dataFactory.getOWLNothing());
		try {
			final Set<Explanation<OWLAxiom>> incExplanation = gen.getExplanations(inc, 10);
			if (!incExplanation.isEmpty()) {
				StringWriter sw = new StringWriter();
				incExplanation.forEach(e -> {
					sw.append(e.getAxioms().toString()).append("\n");
				});
				logger.error("The ontology is inconsistent.", "Explanations from OWLApi: " + sw.toString());
				throw new SAREFPipelineException("The ontology is inconsistent.",
						"Explanations from OWLApi: " + sw.toString());
			}
		} catch (Exception ex) {
			logger.error("Error while reasoning with the ontology", ex);
			throw new SAREFPipelineException("Error while reasoning with the ontology", ex);
		}
		final EntailmentCheckerFactory<OWLAxiom> checker = new SatisfiabilityEntailmentCheckerFactory(reasonerFactory,
				m);
		final ExpansionStrategy<OWLAxiom> expansionStrategy = new StructuralTypePriorityExpansionStrategy<>(
				InitialEntailmentCheckStrategy.PERFORM, m);
		final ContractionStrategy<OWLAxiom> contractionStrategy = new DivideAndConquerContractionStrategy<>();
		final Configuration<OWLAxiom> config = new Configuration<>(checker, expansionStrategy, contractionStrategy,
				null, m);
		final ExplanationGeneratorFactory<OWLAxiom> explanationGeneratorFactory = new BlackBoxExplanationGeneratorFactory<OWLAxiom>(
				config);
		final ExplanationGenerator<OWLAxiom> fgen = explanationGeneratorFactory.createExplanationGenerator(ontology);
		ontology.classesInSignature().forEach(c -> {
			OWLAxiom axiom = dataFactory.getOWLSubClassOfAxiom(c, dataFactory.getOWLNothing());
			Set<Explanation<OWLAxiom>> explanation = fgen.getExplanations(axiom, 10);
			if (!explanation.isEmpty()) {
				StringWriter sw = new StringWriter();
				explanation.forEach(e -> {
					sw.append(e.getAxioms().toString()).append("\n");
				});
				logger.warn(String.format("Class %s cannot be satisfied.", c),
						"Explanations from OWLApi " + sw.toString());
			}
		});
	}

}
