package uk.ac.man.cs.lethe.abduction

import org.semanticweb.HermiT.ReasonerFactory
import org.semanticweb.owlapi.model.{OWLAxiom, OWLEntity, OWLOntology}
import uk.ac.man.cs.lethe.internal.dl.datatypes.Ontology
import uk.ac.man.cs.lethe.internal.dl.owlapi.{ModuleExtractor, OWLApiConverter}
import uk.ac.man.cs.lethe.internal.tools.formatting.SimpleDLFormatter

import java.util
import scala.collection.JavaConverters._

class ObservationEntailedException(message: String) extends Exception(message)

class OWLAbducer {

  val innerAbducer = new FullAbducer();

  var optimizeUsingModules = true

  var ontology: OWLOntology = _
  var abducibles: Set[OWLEntity] = _

  private var checkEntailed = true

  def setCheckEntailed(value: Boolean) =
    checkEntailed=value

  def setBackgroundOntology(ontology: OWLOntology) = {

    this.ontology=ontology

    innerAbducer.setBackgroundOntology(OWLApiConverter.convert(ontology))
  }

  def setAbducibles(abducibles: java.util.Set[OWLEntity]) = {
    this.abducibles = abducibles.asScala.toSet
    val converted = this.abducibles.map(OWLApiConverter.getName).toSet
    innerAbducer.setAbducibles(converted)
  }

  def entailed(observation: util.Set[OWLAxiom]): Boolean = {
    val reasoner = new ReasonerFactory().createReasoner(ontology)
    observation.asScala.forall(reasoner.isEntailed(_))
  }

  def abduce(observation: java.util.Set[OWLAxiom]) = {

    if(checkEntailed) {
      if(entailed(observation))
        throw new ObservationEntailedException("Observation is already entailed!")
    }

    val oldOntology = ontology
    if(optimizeUsingModules){
      val signature = observation.asScala.flatMap(_.getSignature().asScala) ++ abducibles
      ontology = ModuleExtractor.getModuleAsOntology(ontology, signature)
    }

    val converted = new Ontology()

    observation.forEach(axiom => converted.addStatements(OWLApiConverter.convert(axiom)))

    val result = innerAbducer.abduce(converted)

    ontology=oldOntology

    result
  }

  def formatAbductionResult(observation: java.util.Set[OWLAxiom]) = {
    SimpleDLFormatter.format(abduce(observation))
  }
}
