
package uk.ac.man.cs.lethe.internal.fol.unification

import scala.collection.immutable.SortedSet

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


import com.typesafe.scalalogging.Logger

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

  val logger = Logger(Subsumer.getClass)

  def subsumes(e1: Expression, e2: Expression): Boolean = { 
    val s = new Substitution(Set())
    val result = subsumes(e1,e2, s)
    logger.debug("Subsumption unifier: " + s.toString)
    result
  }

  def subsume(cs: Iterable[Clause], c: Clause) =
    cs.exists(subsumes(_, c))

  def subsumer(cs: Iterable[Clause], c: Clause) = 
    cs.find(subsumes(_, c))

  def subsumes(c1: Clause, c2: Clause): Boolean = { 

    logger.debug("Checking Subsumption between " + c1.toString + " and " + c2.toString)

    def inner(ls1: SortedSet[Literal], ls2: SortedSet[Literal], s: Substitution): Boolean =  { 
      if(ls1.isEmpty) { 
	logger.debug("Subsumption unifier: " + s.toString)
	true
      } else {
	val l1 = ls1.head
	ls2.exists{  
	  l2 =>
	    logger.trace("candidate: "+ l2.toString)
	  val s2 = s.copy
	  subsumes(l1, l2, s2) && inner(ls1.tail, ls2, s2)	       
	}
      }
    }    

    // additional constraint: c1 must be smaller than c2, in order to be a subsumer
    if(c1.literals.size>c2.literals.size)
      false
    else
      c1.signature.subsetOf(c2.signature) && inner(c1.literals, c2.literals, new Substitution(Set()))
  }  

  def candidates(ls: SortedSet[Literal], l: Literal): Iterable[Literal] = { 
    val lower = l match { 
      case Literal(p, GenericPredicate(n,args)) => Literal(p, GenericPredicate(n, args.map(x => VariableBuilder.newVariable())))
      case Literal(p, IdentityPredicate(a, b)) => Literal(p, IdentityPredicate(VariableBuilder.newVariable(), VariableBuilder.newVariable()))
    }

    for(l2 <- ls.rangeImpl(Some(lower), None) if l2 <= l)
      yield l2
  }

  def properlySubsumes(c1: Clause, c2: Clause): Boolean = { 
    subsumes(c1, c2) && !subsumes(c2, c1)
  }

  private def subsumes(e1: Expression, e2: Expression, s: Substitution): Boolean = { 
    
    logger.trace("matching " + e1.toString + " and " + e2.toString)

    val result = (e1, e2) match { 
      case (c1: Constant, c2: Constant) => c1==c2
      case (v: Variable, term: Term) => { 
	//val term2 = s.apply(term).asInstanceOf[Term]
	s.getSubstitute(v) match { 
	  case Some(t2s) => { 
	    if(t2s == term)
	      true
	    else
	      false
	  }
	  case None => { 
	    s.addSubstitute(v, term)
	    true
	  }
	}
      }
      case (Literal(p1, f1), Literal(p2, f2)) if p1==p2 => subsumes(f1, f2, s)
      case (GenericPredicate(n1, args1), GenericPredicate(n2, args2)) if n1==n2 && args1.size==args2.size => 
	args1.zip(args2).forall(x=> subsumes(x._1, x._2, s))
      case (IdentityPredicate(t1a, t1b), IdentityPredicate(t2a, t2b)) => (subsumes(t1a, t2a, s) && subsumes(t1b, t2b, s)) ||
									 (subsumes(t1a, t2b, s) && subsumes(t2b, t2a, s))
      case (Function(f1, args1), Function(f2, args2)) if f1==f2 && args1.size==args2.size =>
	args1.zip(args2).forall(x => subsumes(x._1, x._2, s))
      case _ => false
    }

    logger.trace("     ... substitution is currently: " + s.toString)

    result
  }
  
}
