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

import uk.ac.man.cs.lethe.internal.fol.datatypes._
import uk.ac.man.cs.lethe.internal.fol.unification.Subsumer
import uk.ac.man.cs.lethe.internal.normalForms.UnstructuralTransformations
import uk.ac.man.cs.lethe.internal.reasoning.FOLProver

//import com.dongxiguo.zeroLog.Logger

import com.typesafe.scalalogging.Logger

object ResolutionProverSingleton extends ResolutionProver


object ResolutionProverLogger { 
//  implicit val (logger, formatter, appender) =
//  ZeroLoggerFactory.newLogger(this)
  val logger = Logger(ResolutionProverLogger.getClass)
}

class ResolutionProver extends FOLProver {
  val logger = ResolutionProverLogger.logger
//  implicit val formatter = ResolutionProverLogger.formatter
//  implicit val appender = ResolutionProverLogger.appender

  protected var clauses: Set[_ <: Clause] = _
  protected var newClauses: Set[_ <: Clause] = _

  var counter: Int = _

  override def steps = counter

  def setFormula(formula: Formula) = setClauses(UnstructuralTransformations.clauses(formula))

  def setClauses(clauses: Set[Clause]) = {
    this.clauses = clauses
    logger.debug("Initial clauses: ")
    clauses.foreach((x: Clause) => logger.debug(x.toString))
    newClauses = clauses
    counter = 0  
  }

  override def inconsistent(formula: Formula): Boolean = { 
    setFormula(formula)
    val result: Boolean = privateSolve()
    logger.info("Resolution finished with " + counter.toString + " rule applications")
    result
  } 

  def inconsistent(clauses: Set[Clause]): Boolean = {
    setClauses(clauses)
    val result: Boolean = privateSolve()
    logger.info("Resolution finished with " + counter.toString + " rule applications")
    result
  }

  protected var newNew: Set[Clause] = _

  protected def privateSolve(): Boolean = {
    while(!newClauses.isEmpty) {
      newNew = Set()
      
      if(findInconsistencies() ||
	 applyFactoringRules() ||
	 applyResolutionRules() ||
	 applyParamodulationRules())
	return true

      newNew = newNew.filterNot(tautology)
      //newNew = newNew.filterNot(subsumed)
      
      logger.trace("Next round, added " + newNew.size.toString + " clauses.")	    
      filterSubsumedClauses()
      clauses = clauses ++ newNew
      logger.trace("We have now " + clauses.size.toString + " clauses.")
      newClauses = newNew	 
    }
    false
  }

  protected def filterSubsumedClauses() = { 
    clauses = clauses.filterNot(cl => Subsumer.subsume(newNew, cl))
  }

  protected def addClause(clause: Clause) = { 
    if(!subsumed(clause) && !tautology(clause))
      newNew += clause
  }

  protected def subsumed(clause: Clause): Boolean = { 
    if(Subsumer.subsume(newNew++clauses, clause)){ 
      logger.trace("subsumed "+clause.toString)
      true
    } else
      false
  }

  protected def tautology(clause: Clause): Boolean = {
    if(clause.literals.exists{ l1 =>
         clause.literals.exists{ l2 =>
	   l1==l2.negated
         }
       }){ 
      logger.trace(clause.toString + " is a tautology!")
      true
    } else
      false
  }
  
  protected def findInconsistencies(): Boolean = { 
    newClauses.exists(_.literals.isEmpty)
  }

  protected def applyFactoringRules(): Boolean = { 
    for(clause <- newClauses;
	literal1 <- clause.literals;
	literal2 <- clause.literals if literal2 != literal1){
      val rule = new FactoringRule(clause, literal1, literal2)
      if(rule.applies){
	counter = counter + 1
	logger.trace("Factoring " + clause.toString + " on " + literal1.toString + " and " + literal2.toString)
	val newClause = rule.apply()
	logger.trace("New clause: " + newClause.toString)
	if(newClause.literals.isEmpty)
	  return true
	else
	  addClause(newClause)
      }
    }
    false
  }

  protected def applyResolutionRules(): Boolean = { 
    for(clause1 <- clauses;
	literal1 <- clause1.literals;
	clause2 <- newClauses if clause2 != clause1;
	literal2 <- clause2.literals){
      val rule = new ResolutionRule(clause1, literal1, clause2, literal2)
      if(rule.applies){
	counter = counter + 1
	logger.trace("Resolving " + clause1.toString + " and " + clause2.toString)
	logger.trace(" on " + literal1.toString + " and " + literal2.toString)
	val newClause = rule.apply()
	logger.trace("New clause: " + newClause.toString)
	if(newClause.isEmpty)
	  return true
	else
	  addClause(newClause)
      }	    
    }
    false
  }

  protected def applyParamodulationRules(): Boolean = { 
    for(clause1 <- clauses;
	identity <- clause1.literals.toList.collect{ case Literal(true, id: IdentityPredicate) => id };
	clause2 <- clauses;
	term <- clause2.terms){
      val rule = new ParamodulationRule(clause1, identity, clause2, term)
      if(rule.applies){
	counter = counter + 1
	logger.trace("paramodulation on " + clause1.toString + " and " + clause2.toString + " on " + identity.toString)
	val newClause = rule.apply()
	logger.trace("New clause: "+newClause)
	if(newClause.isEmpty)
	  return true
	else
	  addClause(newClause)
      }	    		 
    }
    false
  }
}
