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

import uk.ac.man.cs.lethe.internal.dl.datatypes.extended.{ConjunctiveAssertion, ConjunctiveDLStatement, DisjunctiveAssertion, DisjunctiveDLStatement, NegatedRoleAssertion}
import uk.ac.man.cs.lethe.internal.dl.datatypes.{Assertion, BaseRole, BottomConcept, CheapSimplifier, ConceptAssertion, ConceptComplement, ConceptConjunction, ConceptDisjunction, ConceptEquivalence, DLStatement, ExistentialRoleRestriction, Individual, Ontology, OntologyBeautifier, RoleAssertion, Subsumption, TopRole}

object KBNegator {

  var individual: Individual = Individual("a") // individual name used for negated TBox axioms

  /**
   * This negation operator may result in a mixed disjunction of TBox axioms and assertions.
   * assertions with universal roles may be transformed to TBox axioms
   */
  def negate(ontology: Ontology): DLStatement =
    DisjunctiveDLStatement(ontology.statements.map(negate).toSet)

  def negate(statement: DLStatement): DLStatement = statement match {
    case ConceptAssertion(ConceptDisjunction(cs), a: Individual) =>
      negate(DisjunctiveDLStatement(cs.map(ConceptAssertion(_,a))))
    case ConceptAssertion(ConceptConjunction(cs), a: Individual) =>
      negate(ConjunctiveDLStatement(cs.map(ConceptAssertion(_,a))))
    case ConceptAssertion(ExistentialRoleRestriction(TopRole, concept), _) =>
      OntologyBeautifier.nice(Subsumption(concept, BottomConcept))
    case DisjunctiveAssertion(disjuncts) =>
      negate(DisjunctiveDLStatement(disjuncts.toSet[DLStatement]))
    case DisjunctiveDLStatement(disjuncts) =>
      ConjunctiveDLStatement(disjuncts.map(negate))
    case ConjunctiveAssertion(conjuncts) =>
      negate(ConjunctiveDLStatement(conjuncts.toSet[DLStatement]))
    case ConjunctiveDLStatement(conjuncts) =>
      DisjunctiveDLStatement(conjuncts.map(negate))
    case other =>
      negate2Assertion(other)
  }

  /**
   * This negation operator always results in a disjunction of assertions.
   */
  def negate2Assertion(ontology: Ontology): Assertion =
    negate2Assertion(ontology.statements)

  def negate2Assertion(statements: Iterable[DLStatement]): Assertion =
    DisjunctiveAssertion(statements.map(negate2Assertion).toSet)

  def negate2Assertion(statement: DLStatement): Assertion = statement match {
    case ConceptAssertion(c, a) =>
      ConceptAssertion(CheapSimplifier.simplify(ConceptComplement(c)), a)
    case RoleAssertion(r: BaseRole, a, b) => NegatedRoleAssertion(r, a, b)
    case NegatedRoleAssertion(r, a, b) => RoleAssertion(r, a, b)
    case Subsumption(c, d) =>
      ConceptAssertion(CheapSimplifier.simplify(ExistentialRoleRestriction(TopRole,
        ConceptConjunction(Set(c, ConceptComplement(d))))), individual)
    case DisjunctiveAssertion(disjuncts) =>
      ConjunctiveAssertion(disjuncts.map(negate2Assertion))
    case ConceptEquivalence(c1, c2) =>
      DisjunctiveAssertion(Set(negate2Assertion(Subsumption(c1,c2)), negate2Assertion(Subsumption(c2,c1))))
  }

}
