
package uk.ac.man.cs.lethe.internal.resolution.constraintResolution

import uk.ac.man.cs.lethe.internal.fol.datatypes._
import uk.ac.man.cs.lethe.internal.resolution._
import uk.ac.man.cs.lethe.internal.resolution.simplifications.SimplificationRule
import uk.ac.man.cs.lethe.internal.fol.unification._

import com.typesafe.scalalogging.Logger

/***
 * Rules for constraint resolution
 *
 * TODO:
 *  Condensation rule  
 * */


object CResolutionRuleGetter extends RuleGetter {
//  implicit val (logger, formatter, appender) = ZeroLoggerFactory.newLogger(this)

  val logger = Logger(CResolutionRuleGetter.getClass)

  override def getRules(clauses: Iterable[Clause], clause: Clause) = 
    for(clause1 <- clauses;
	literal1 <- clause1.literals;
	literal2 <- clause.literals)
    yield new CResolutionRule(clause1, literal1, clause, literal2)
}

class CResolutionRule(var clause1: Clause, var literal1: Literal, var clause2: Clause, var literal2: Literal) extends Rule {
//  import CResolutionRuleGetter.formatter._
  val logger = CResolutionRuleGetter.logger

  override val selectedLiteral = literal2
  override val mainClause = clause2
  override val clauses = List(clause1, clause2)

  override def doesApply = { 
    (literal1.positive == !literal2.positive && 
     literal1.predicate.name == literal2.predicate.name && 
     literal1.predicate.args.size == literal2.predicate.args.size && 
     FolHelpers.differentVariables(literal1.predicate, literal2.predicate))
  }

  override def apply() = {
    assert(applies)

    var result = clause1.literals - literal1 ++ clause2.literals - literal2

    for(i <- 0 to literal1.predicate.args.size-1){ 
      result = result + new Literal(false, new IdentityPredicate(literal1.predicate.args(i), literal2.predicate.args(i)))
    }

    new Clause(result)
  }

  override def toString = { 
    "Constraint resolution on "+clause1.toString+" and "+clause2.toString
  }
}

object CFactoringRuleGetter extends RuleGetter { 
//  implicit val (logger, formatter, appender) =
//  ZeroLoggerFactory.newLogger(this)

  val logger = Logger(CFactoringRuleGetter.getClass)

  override def getRules(clauses: Iterable[Clause], clause: Clause) = 
    for(literal1 <- clause.literals;
	literal2 <- clause.literals if literal1.positive==literal2.positive)
    yield new CFactoringRule(clause, literal1, literal2)  
}

class CFactoringRule(var clause: Clause, var literal1: Literal, var literal2: Literal) extends Rule { 
  import CFactoringRuleGetter._

  override val selectedLiteral = literal2
  override val mainClause = clause
  override val clauses = List(clause)

  override def doesApply = { 
    (literal1.positive == literal2.positive &&
     literal1.predicate.name == literal2.predicate.name &&
     literal1.predicate.args.size == literal2.predicate.args.size &&
     FolHelpers.differentVariables(literal1.predicate, literal2.predicate))
  }

  override def apply() = {
    assert(applies)

    var result = clause.literals - literal2

    logger.trace("applying on "+literal1.toString+" and "+literal2.toString)

    for(i <- 0 to literal1.predicate.args.size-1){ 
      result = result + new Literal(false, new IdentityPredicate(literal1.predicate.args(i), literal2.predicate.args(i)))
    }

    new Clause(result)
  }  

  override def toString = { 
    "Constraint factoring on "+clause
  }
}


object ConstraintElimination extends SimplificationRule { 
//  implicit val (logger, formatter, appender) = ZeroLoggerFactory.newLogger(this)

  val logger = Logger(ConstraintElimination.getClass)

  override def simplify(clause: Clause) = { 
    val iLiterals = clause.literals.toList.collect(x => x match { case Literal(p, _: IdentityPredicate) if !p => x})
    // <- conversion to list is neccessary to allow creation of literal sequence later

    var result = clause.literals -- iLiterals

    val identities = iLiterals.collect(_ match { case Literal(_, x: IdentityPredicate) => x})

 //map(_.predicate.asInstanceOf[IdentityPredicate])

    identities.foreach{ x =>
      try{ 
	val unifier = Unifier.unify(x.arg1, x.arg2)

	logger.trace("applying "+unifier.toString+" on "+clause.toString)
	
	result = result.map(unifier.apply)
      } catch { 
	case _: NotMatchableException => result = result + new Literal(false, x) // ACHTUNG! Vorherige Unifizierungen?
      }
    }		     
    new Clause(result)
  }
}
