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

import uk.ac.man.cs.lethe.internal.fol.datatypes._
import uk.ac.man.cs.lethe.internal.fol.unification.Substitut

import com.typesafe.scalalogging.Logger

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

  val logger = Logger(Skolemizer.getClass)

  def skolemize(formula: Formula): Formula =  {

    def inner(formula: Formula, vars: List[Variable]): Formula = formula match {  
      case _: Literal => formula
      case Negation(f) => Negation(inner(f, vars))
      case Disjunction(ds) => Disjunction(ds.map(inner(_, vars)))
      case Conjunction(cs) => Conjunction(cs.map(inner(_, vars)))
      case Implication(a,b) => Implication(inner(a, vars), inner(b, vars))
      case Equivalence(a,b) => Equivalence(inner(a, vars), inner(b, vars))
      case UniversalQuantification(v,f) => {
	// Do skolemization and universal quantifier removal in one step
	// (rename variable and keep track of new variable for skolem terms) 
	val v2 = VariableBuilder.newVariable()
	val f2 = new Substitut(v,v2).apply(f)
	inner(f2, v2::vars)
      }
      case ExistentialQuantification(v,f) => { 
	val term = TermBuilder.newTerm(vars)
	val f2 = new Substitut(v, term).apply(f)
	inner(f2, vars)
      }
    }

    inner(formula, List())
  }

  def skolemizedPNF(formula: Formula): Formula = { 
    val pnf = Prenexing.prenexNormalForm(formula)
    logger.debug("PNF: " + pnf.toString)


    var variables: List[Variable] = List()

    def inner(formula: Formula): Formula = formula match { 
      case ExistentialQuantification(v,f) => { 
	val term = TermBuilder.newTerm(variables)
	val fx = new Substitut(v, term).apply(f)
	inner(fx)
      }
      case UniversalQuantification(v,f) => { 
	variables = v::variables
	inner(f)
      }
      case _ => formula
    }

    inner(pnf)
  }
  
}

object TermBuilder {
  var counter = 0
  def newTerm(variables: List[Variable]): Term = {
    counter = counter + 1
    if(variables.isEmpty)
      new Constant("_c"+counter)
    else
      new Function("_f"+counter, variables)
  }
  
}
