
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.{ ClauseOrdering, RestrictedOrdering }
import uk.ac.man.cs.lethe.internal.resolution.simplifications._

import com.typesafe.scalalogging.Logger

object SimpleScanForgetterSingleton extends SimpleScanForgetter 


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

  val logger = Logger(SimpleScanForgetterLogger.getClass)
}

class SimpleScanForgetter extends Forgetter[Iterable[Clause], String] { 
  import SimpleScanForgetterLogger._

  val MAX_STEPS = 10000

  protected var newClauses: Set[Clause] = _
  protected var processedClauses: Set[Clause] = _
  protected var oldClauses: Set[Clause] = _
  protected var result: Set[Clause] = _
  protected var ignored: Set[Clause] = _

  protected var nonBaseSymbols: Set[String] = _

  protected var ruleGetter: RuleGetter = _
  protected val simplificationRule: SimplificationRule = TautologyDeletion

  var counter: Int = _

  override def steps = counter

  protected def setClauses(clauses: Set[Clause]) = {
    logger.debug("Initial clauses: ")
    clauses.foreach((x: Clause) => logger.debug(x.toString))
    newClauses = clauses.filter(!_.signature.intersect(nonBaseSymbols).isEmpty)
    ignored = clauses -- newClauses
    processedClauses = emptySet
    oldClauses = emptySet
    result = emptySet
    counter = 0  
  }

  def setNonBaseSymbols(nonBaseSymbols: Set[String]) = { 
    logger.info("Non-base symbols: " + nonBaseSymbols.mkString("{",", ","}"))
    this.nonBaseSymbols=nonBaseSymbols
    setRuleGetter()
  }


  protected def setRuleGetter() { 
    ruleGetter = new CombinedRuleGetter(List(new RestrictedRuleGetter(ResolutionRuleGetter, nonBaseSymbols),
					     new RestrictedRuleGetter(FactoringRuleGetter, nonBaseSymbols)))
  }

  override final def forget(clauses: Iterable[Clause], predicates: Set[String]) = { 
    setNonBaseSymbols(predicates)
    setClauses(clauses.toSet)
    
    while(!finished)
      resolve()

    logger.debug("Forgetting result: ")
    result.foreach(x => logger.debug("  " + x.toString))
    
    result++ignored //FolHelpers.simplify(FolHelpers.toFormula(result++ignored))
  }

  protected def logCurrentState() = { 
    logger.trace(" new clauses: " + newClauses.mkString("\n\t","\n\t","\n"))
    logger.trace(" processed clauses: " + processedClauses.mkString("\n\t","\n\t","\n"))
    logger.trace(" old clauses: " + oldClauses.mkString("\n\t","\n\t","\n"))
    logger.trace(" result: " + result.mkString("\n\t", "\n\t", "\n"))
    logger.trace(" steps until now: " ++ steps.toString)
  }

  protected def emptySet: Set[Clause] = Set()

  protected def resolve(): Unit = {     
    logCurrentState()
      
      // Process clauses
    processedClauses = processedClauses ++ newClauses
    newClauses = emptySet

      // Compute inferences
    while(!processedClauses.isEmpty){ 
      
      if(steps>MAX_STEPS){ 
	breakExecution()
	return
      }
	  
      logCurrentState()

      val nextClause = processedClauses.head
      logger.trace(" - Current clause: " + nextClause.toString)
      if(shouldProcess(nextClause)){ 
	for(rule <- ruleGetter.getRules(oldClauses, nextClause)){ 
	  if(rule.applies) { 
	    logger.trace("  -> " + rule.toString)
	    var newClause = Unifier.makeVarsUnique(rule.apply)._2
	    addNewClause(newClause)
	    newClauses = simplificationRule.simplify(newClauses)
	    forwardSubsumption()
	    backwardSubsumption()
	  }
	  // else
	  //   logger.trace("  " + rule.toString ++= " does not apply")
	}
      }
      else { 
	logger.trace("  -> added to result")
	result = result + nextClause
      }
      processedClauses = processedClauses - nextClause
      oldClauses = oldClauses + nextClause
    }
    logCurrentState()
    logger.trace(" simplifying... ")
  }


  protected def finished: Boolean = 
    newClauses.isEmpty && processedClauses.isEmpty
  
  protected def shouldProcess(clause: Clause) = 
    hasNonBaseSymbols(clause)

  protected def condense(clause: Clause): Clause = { 
    FactoringRuleGetter.getRules(Set(), clause).collectFirst{ 
      case rule if rule.applies && Subsumer.subsumes(rule.apply(), clause) => 
	logger.trace(rule.toString)
	rule.apply()
    } match { 
      case Some(simplified) => condense(simplified)
      case None => clause
    }
  }

  protected def addNewClause(clause: Clause): Unit = { 
    val condensed = condense(clause)
    logger.trace("   + New clause: " + condensed.toString)
    newClauses += condensed
    counter = counter + 1
  }

  protected def breakExecution() = { 
    println("Execution stopped")
    //result = Set()
  }

  private def hasNonBaseSymbols(clause: Clause): Boolean = { 
    clause.literals.exists(l => nonBaseSymbols.contains(l.predicate.name))
  }

  protected def forwardSubsumption() = { 
    logger.trace("Forward subsumption...")
    newClauses = newClauses.filterNot(x=> if(subsumes(processedClauses ++ oldClauses)(x))
      { logger.trace("removing "+ x.toString); true}
					  else false)
  }

  protected def backwardSubsumption() = { 
    logger.trace("Backward subsumption...")
    processedClauses = processedClauses.filterNot(x => if(properlySubsumes(newClauses)(x))
      { logger.trace("removing processed clause "+x.toString); true}
						  else false)
    oldClauses = oldClauses.filterNot(x => if(properlySubsumes(newClauses)(x))
      { logger.trace("removing old clause "+x.toString); true}
						  else false)
  }

  protected def subsumes(clauses: Set[Clause]) = { 
    x: Clause =>
      clauses.exists(Subsumer.subsumes(_, x))
  }

  protected def properlySubsumes(clauses: Set[Clause]) = { 
    x:Clause =>
      clauses.exists(Subsumer.properlySubsumes(_, x))
  }
}
