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

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

import scala.collection.immutable.{TreeSet, SortedSet}
import scala.util.control.Breaks._

import com.typesafe.scalalogging.Logger

object OrderedResolutionProverSingleton extends OrderedResolutionProver()(LiteralOrdering)

class OrderedResolutionProver(implicit ordering: ProperLiteralOrdering) extends ResolutionProver { 
//  override implicit val (logger, formatter, appender) =
//  ZeroLoggerFactory.newLogger(OrderedResolutionProverSingleton)

  override val logger = Logger[OrderedResolutionProver]

  override def setClauses(clauses: Set[Clause]) = { 
    super.setClauses(clauses.map(x => new SortedClause(x.literals)(ordering)))
  }

  override def applyResolutionRules(): Boolean = { 
    for(clause1 <- clauses;
	literal1 <- clause1.literals;
	clause2 <- newClauses if clause2 != clause1;
	literal2 <- clause2.literals){
      val rule = new OrderedResolutionRule(clause1.asInstanceOf[SortedClause], 
					   literal1, 
					   clause2.asInstanceOf[SortedClause], 
					   literal2,
					 ordering)
      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
  }

  override def addClause(clause: Clause) = 
    super.addClause(new SortedClause(clause)(ordering))
}

// companion object
object OrderedResolutionProver { 
  implicit val stdOrdering: ProperLiteralOrdering = LiteralOrdering
}

class OrderedResolutionRule(clause1:SortedClause, literal1:Literal, clause2:SortedClause, literal2:Literal,
			  ordering: ProperLiteralOrdering) 
extends ResolutionRule(clause1, literal1, clause2, literal2) { 

  // clause1 = new SortedClause(clause1.literals)
  // clause2 = new SortedClause(clause2.literals)

  override def doesApply = hasMaximalAtom(clause1, literal1) && hasMaximalAtom(clause2, literal2) && super.doesApply

  override def apply() = { 
    val result = new SortedClause(super.apply())(clause1.ordering)
    // ensure we are minimizing on the main clause
    // assert(ClauseOrdering.compare(result, clause1)<0, 
    // 	   "result "+result+" of resolving "+clause1.toString+" and "+clause2.toString+" is not smaller than first clause")

    // assert(ClauseOrdering.compare(result, clause2)<=0, 
    // 	   "result "+result+" of resolving "+clause1.toString+" and "+clause2.toString+" is not smaller than main clause")
    result
  }


  private def hasMaximalAtom(c: Clause, l: Literal): Boolean = { 
    (c match { 
      case s: SortedClause => l==s.literals.head 
      case _ => c.literals.max(LiteralOrdering)==l
    }) //&& !c.literals.exists(ordering.bigger(l, _))
    // val l2 = l//new Literal(false, l.predicate)
    
    // LiteralOrdering.compare(l2, c.literals.head) >= 0
  }
}

object ClauseOrdering extends Ordering[SortedClause] { 
  override def compare(c1: SortedClause, c2: SortedClause) = c1.toString.compare(c2.toString)
}

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

class SortedClause(_literals: Set[Literal])(implicit _ordering: Ordering[Literal]) extends Clause(_literals) { 
  val logger = SortedClauseLogger.logger
//  import SortedClauseLogger.formatter._

  implicit val stdOrdering = LiteralOrdering

  val ordering = _ordering

//  def this(_literals: Set[Literal]) = this(_literals, LiteralOrdering)

  def this(clause: Clause)(implicit ordering: Ordering[Literal]) = this(clause.literals)(ordering)


  override val literalsSorted: SortedSet[Literal] = TreeSet()(ordering.reverse) ++ _literals

  override def literals = literalsSorted

  override def toString = literals.mkString(" || ")
}

abstract class ProperLiteralOrdering extends Ordering[Literal]  { 
  def bigger(l1: Literal, l2: Literal): Boolean = { 
    compare(l1, l2) == 1
  }
}

object LiteralOrdering extends ProperLiteralOrdering { 
  override def compare(l1: Literal, l2: Literal) = ExpressionOrdering.compare(l1,l2)
}

object ExpressionOrdering extends Ordering[Expression] { 
  override def compare(e1: Expression, e2: Expression) = (e1, e2) match { 
    case (Literal(_,p1), Literal(_,p2)) if compare(p1,p2)!=0 => compare(p1,p2)
    case (Literal(false,_), Literal(true,_)) => -1
    case (Literal(true,_), Literal(false,_)) => 1
    case (_:Literal, _:Literal) => 0
    case (Variable(v1), Variable(v2)) => v1.compareTo(v2)
    case (Constant(c1), Constant(c2)) => c1.compareTo(c2)
    case (_:Variable, _:Constant) => 1
    case (_:Constant, _:Variable) => -1
    case (Function(f1, l1), Function(f2, l2)) if f1==f2 => compare(l1,l2)
    case (Function(f1, _), Function(f2, _)) => f1.compareTo(f2)
    case (_: Function, _: Term) => -1
    case (_:Term, _:Function) => 1
    case (p1: Predicate, p2: Predicate) if p1.name==p2.name => compare(p1.args,p2.args)
    case (p1: Predicate, p2: Predicate) => p1.name.compareTo(p2.name)
  }

  def compare(l1: List[Term], l2: List[Term]): Int = (l1, l2) match { 
    case (Nil, Nil) => 0
    case (Nil, _) => -1
    case (_, Nil) => 1
    case (t1::tail1, t2::tail2) => { 
      val c = compare(t1,t2)
      if(c==0)
	compare(tail1, tail2)
      else
	c
    }
  }
}
