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

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

import com.typesafe.scalalogging.Logger

object UnstructuralTransformations extends ClausesGenerator { 

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

  val logger = Logger(UnstructuralTransformations.getClass)

  def clauses(formula: Formula): Set[Clause] = { 

    def clause(x: Formula): Clause = x match { 
      case x: Disjunction => new Clause(x.disjuncts.map(x => x.asInstanceOf[Literal]))
      case x: Literal => new Clause(Set(x))
    }

    val cnf = conjunctiveNormalForm(formula)
    logger.debug("CNF: " +cnf.toString)
    cnf.conjuncts.map((x: Formula) => clause(x))
  }

  def conjunctiveNormalForm(formula: Formula): Conjunction = {
    
    val f = Skolemizer.skolemize(Prenexing.antiPrenexNormalForm(formula))
        
    logger.debug("skolem: " +f.toString)

    // Assumptions until now: no quantifiers, no implications, no negations
    def inner(formula: Formula): Conjunction = formula match { 
      case _: Literal => new Conjunction(Set(formula))
      case Conjunction(cs) => new Conjunction(cs.map(inner))
      case Disjunction(ds) => { 
	val c = ds.find(_.isInstanceOf[Conjunction]) 
	c match { 
	  case None => new Conjunction(Set(formula))
	  case Some(conjunction:Conjunction) => { 
	    val ds2 = ds - conjunction
	    new Conjunction(conjunction.conjuncts.map(x => inner(new Disjunction(ds2 + x))))
	  }
	}
      }
      case _ => { 
	assert(false, "Formula not in expected form: "+formula)
	new Conjunction(Set())
      }
    }

    inner(f)
  }
  
  def removeImplicationAndEquivalence(formula: Formula): Formula = {

    def inner(formula: Formula): Formula = formula match { 
      case Equivalence(f1, f2) => new Conjunction(Set(new Disjunction(Set(new Negation(inner(f1)), inner(f2))),
						      new Disjunction(Set(inner(f1), new Negation(inner(f2))))))
      case Implication(f1, f2) => new Disjunction(Set(new Negation(inner(f1)), inner(f2)))
      case Negation(negated) => negated match { 
	case Equivalence(f1, f2) => new Conjunction(Set(new Disjunction(Set(new Negation(inner(f1)), new Negation(inner(f2)))),
							new Disjunction(Set(inner(f1), inner(f2)))))
	case Implication(f1, f2) => new Conjunction(Set(inner(f1), new Negation(inner(f2))))
	case _ => new Negation(inner(negated))
      }
      case Disjunction(ds) => new Disjunction(ds.map(inner))
      case Conjunction(cs) => new Conjunction(cs.map(inner))
      case ExistentialQuantification(v, f) => new ExistentialQuantification(v, inner(f))
      case UniversalQuantification(v, f) => new UniversalQuantification(v, inner(f))
      case _ => formula
    }
    
    logger.debug("Formula: " +formula.toString)

    inner(formula)
  }

  def negationNormalForm(formula: Formula): Formula = { 
    def inner(formula: Formula): Formula = formula match { 
      case Conjunction(cs) => new Conjunction(cs.map(inner))
      case Disjunction(ds) => new Disjunction(ds.map(inner))
      case ExistentialQuantification(v,f) => new ExistentialQuantification(v, inner(f))
      case UniversalQuantification(v,f) => new UniversalQuantification(v, inner(f))
      case Negation(formula2) => formula2 match { 
	case Negation(formula3) => inner(formula3)
	case Literal(pos, pred) => Literal(!pos,pred)
	case Conjunction(cs) => Disjunction(cs.map((x: Formula) => inner(new Negation(x))))
	case Disjunction(ds) => Conjunction(ds.map((x: Formula) => inner(new Negation(x))))
	case ExistentialQuantification(v,f) => UniversalQuantification(v, inner(new Negation(f)))
	case UniversalQuantification(v,f) => ExistentialQuantification(v, inner(new Negation(f)))
	case _ => formula
      }
      case _ => formula
    }

    val f2 = removeImplicationAndEquivalence(formula)

    logger.debug("remove impls: "+f2.toString)

    inner(f2)
  }
}

