
package uk.ac.man.cs.lethe.internal.forgetting.scan

import scala.collection.immutable.TreeSet

import uk.ac.man.cs.lethe.internal.fol.datatypes._
import uk.ac.man.cs.lethe.internal.fol.unification.{ Unifier, Subsumer }
import uk.ac.man.cs.lethe.internal.forgetting.Forgetter
import uk.ac.man.cs.lethe.internal.resolution._
import uk.ac.man.cs.lethe.internal.resolution.orderings._
import uk.ac.man.cs.lethe.internal.resolution.simplifications._


trait ExtendedSuccessCriterion extends OrderedSimpleScanForgetter { 

  override def shouldProcess(clause: Clause) = 
    true

  override def finished = 
    (newClauses++processedClauses).forall(ofDesiredForm)

  protected def ofDesiredForm(clause: Clause) = { 
    clause.literals.forall(_.signature.intersect(nonBaseSymbols).isEmpty)
  } 

  override def setClauses(clauses: Set[Clause]) = { 
    super.setClauses(clauses)
    newClauses = newClauses ++ ignored // to make full benefit of this approach, we add back ignored clauses
    ignored = emptySet
  }

  override def setNonBaseSymbols(nonBaseSymbols: Set[String]) = { 
    super.setNonBaseSymbols(nonBaseSymbols)
    restrictedOrdering = new DepthBasedOrdering(restrictedOrdering)
    clauseOrdering = new ClauseOrdering(restrictedOrdering).reverse
  }

  override def setRuleGetter() { 
    //super.setRuleGetter()
    ruleGetter = new CombinedRuleGetter(List(OrderedResolutionRuleGetter,
					     FactoringRuleGetter))
  }

}

class OrderedSimpleScanForgetter(ordering: Ordering[Literal], aposteriori: Boolean) extends SimpleScanForgetter { 

  var restrictedOrdering: Ordering[Literal] = _
  var clauseOrdering: Ordering[Clause] = _

  protected var origOrdering: Ordering[Literal] = ordering

  override def setNonBaseSymbols(nonBaseSymbols: Set[String]) = { 
    super.setNonBaseSymbols(nonBaseSymbols)

    restrictedOrdering = new RestrictedOrdering(ordering, nonBaseSymbols)
    clauseOrdering = new ClauseOrdering(restrictedOrdering).reverse
    setRuleGetter()
  }

  override def setClauses(clauses: Set[Clause]) = { 
    var ordered = TreeSet()(clauseOrdering) ++ clauses.toSeq.map(c => new SortedClause(c)(restrictedOrdering))
    super.setClauses(ordered)
  }

  override def setRuleGetter() { 
    //super.setRuleGetter()
    ruleGetter = new RestrictedRuleGetter(new CombinedRuleGetter(List(OrderedResolutionRuleGetter,
								  FactoringRuleGetter)), 
							     nonBaseSymbols)
    ruleGetter = new ConstrainedRuleGetter(ruleGetter, List(//new OnlyOnMaximalLiteral(clauseOrdering, false),
							    NoNestedSkolemTerms
							    //OnlyProperALCRoles
							  ))
  }

  override def addNewClause(clause: Clause) = 
    super.addNewClause(new SortedClause(clause)(restrictedOrdering))

  override def emptySet = 
    TreeSet()(clauseOrdering)
}

// Removes rules that leads to nested skolem terms using the same function (eg. f(f(x)))
class ConstrainedRuleGetter(getter: RuleGetter, constraints: Iterable[RuleConstraint]) extends RuleGetter { 
  override def getRules(clauses: Iterable[Clause], clause: Clause) = { 
    getter.getRules(clauses, clause).map(new ConstrainedRule(_, constraints))
  }
}

/**
 * Beschraenkt die Anwendbarkeit einer Regel
 */
class ConstrainedRule(rule: Rule, constraints: Iterable[RuleConstraint]) extends Rule { 

  assert(rule!=null)
  assert(constraints!=null)

  var result: Option[Clause] = _
  
  def getResult = result = if(rule.applies) 
    Some(rule.apply)
  else
    None

  override def doesApply = {
    getResult
    rule.applies && constraints.forall(_.allowed(rule, result.get))
  }

  override def apply = 
    if(!applies) 
      throw new RuleNotApplicableException()
    else
      result.get

  override val clauses = rule.clauses
  override val mainClause = rule.mainClause
  override val selectedLiteral = rule.selectedLiteral
  override def toString = "Restricted "+rule.toString
}

abstract class RuleConstraint { 
  def allowed(rule: Rule, result: Clause): Boolean
}

/**
 * Restricts to clauses in which each binary predicate can be backtranslated to ALC
 */
object OnlyProperALCRoles extends RuleConstraint { 
  override def allowed(rule: Rule, result: Clause) = { 
    result.literals.forall(l => l.predicate.args!=2 || properALCRole(l.predicate))
  }

  def properALCRole(predicate: Predicate): Boolean = { 
    predicate.args match { 
      case term1::Function(_, args)::Nil if args.contains(term1) => true
      case term1::(v: Variable)::Nil => true
      case _ => false
    }
  }
}

/**
 * Only allows rules that don't create nested occurrences of the same skolem function
 */
object NoNestedSkolemTerms extends RuleConstraint { 
  override def allowed(rule: Rule, result: Clause) = { 
    result.literals.forall(!nested(_))
  }

  def nested(l: Literal) = { 
    def nested(term: Term, symbols: Set[String]): Boolean = term match { 
      case Function(n, args) => symbols.contains(n) || args.exists(nested(_, symbols+n))
      case _ => false
    }
    val result = l.predicate.args.exists(nested(_, Set()))
    if(result)
      println("avoid "+l)
    result
  }
}

// Only allow rules that work on maximal literals
// Assumption: Clauses are sorted by the literal ordering underlying the given ordering
// @param posteriori - only return rules where the resulting clause is smaller
class OnlyOnMaximalLiteral(ordering: Ordering[Clause], aposteriori: Boolean) extends RuleConstraint { 
  override def allowed(rule: Rule, result: Clause) = { 
    if(!aposteriori)
      rule.selectedLiteral==rule.mainClause.literals.head
    else
      ordering.compare(result, rule.mainClause)<0
  }
}

