package uk.ac.man.cs.lethe.internal.application.benchmarks

import uk.ac.man.cs.lethe.internal.dl.owlapi.ModuleExtractor
import uk.ac.man.cs.lethe.internal.dl.owlapi.{OWLApiConverter, OWLApiInterface, OWLReasonerFacade}

import java.io.File
import java.util.Date
import scala.concurrent._
import scala.concurrent.duration._
import scala.util.Random
import org.semanticweb.owlapi.model.OWLOntology
import uk.ac.man.cs.lethe.internal.application.benchmarks.CustomExecutionContext
import uk.ac.man.cs.lethe.internal.dl.datatypes._
import uk.ac.man.cs.lethe.internal.dl.filters.OWLOntologyFilters
import uk.ac.man.cs.lethe.internal.dl.forgetting.DirectALCForgetter

object AbductionExperimenter {

  implicit val exec = new CustomExecutionContext()

  val universalRole = BaseRole("***UNIVERSAL***")

  def experimentalRun(owlOntology: OWLOntology, timeout: Duration): Unit = { 
    val restricted = ModuleExtractor.prepare(owlOntology)
    var ontologyFragment: Ontology = null
    while(ontologyFragment==null||ontologyFragment.tbox.axioms.size==0) { 
      val owlModule = ModuleExtractor.randomGenuineModule(restricted)
      ontologyFragment = Ontology.buildFrom(owlModule.flatMap(OWLApiConverter.convert))
    }


    
    println("Module size: "+ontologyFragment.statements.size)

    if(ontologyFragment.tbox.axioms.size<2){ 
      println("Singleton TBox - abduction not meaningful")
      return
    }

    val (conceptExpression,statement) = extractRandomConceptExpression(ontologyFragment)

    ontologyFragment.remove(statement)
    

    println("Concept size: "+conceptExpression.size)

    val extStatement = Subsumption(TopConcept, 
				   ExistentialRoleRestriction(universalRole,
							      conceptExpression))
    val negated = Subsumption(TopConcept, ConceptComplement(conceptExpression))

    val signature = BackgroundOntologyForgettingExperimenter.pickSignature(ontologyFragment)
    println("Signature size: "+signature.size)
    val forget = ontologyFragment.atomicConcepts -- signature
    println("Forget: "+forget.size)
    println()
    val startExt = new Date().getTime
    
    val fExtResult = Future[Ontology]{ 
      DirectALCForgetter.forget(ontology(extStatement), forget, ontologyFragment)
    }
    try { 
      val result = Await.result(fExtResult, timeout)
      val duration = new Date().getTime-startExt

      println("Output Ext statements: "+result.statements.size)
      println("Output Ext avg axiom size: "+(result.size.toDouble/result.statements.size))
      if(result.statements.size>0)
	println("Output Ext max statement size: "+result.statements.map(_.size).max)
      println("Output Ext signature: "+result.atomicConcepts.size)
      println("Duration Ext: "+(duration.toDouble/1000)+"sec.")
      if(result.atomicConcepts.exists(!signature(_)))
	println("not in signature: "+result.atomicConcepts.filter(forget).mkString(", "))
    } catch { 
      case _: TimeoutException => 
	println("Ext Timeout.")
      exec.lastThread.getOrElse(throw new RuntimeException("Not started")).stop()
    }
    println()
    val startNeg = new Date().getTime
    val fNegResult = Future[Ontology]{ 
      DirectALCForgetter.forget(ontology(negated), forget, ontologyFragment)
    }
    try { 
      val result = Await.result(fNegResult, timeout)
      val duration = new Date().getTime-startNeg

      println("Output Neg statements: "+result.statements.size)
      println("Output Neg avg axiom size: "+(result.size.toDouble/result.statements.size))
      if(result.statements.size>0)
	println("Output Neg max statement size: "+result.statements.map(_.size).max)
      println("Output Neg signature: "+result.atomicConcepts.size)
      println("Duration Neg: "+(duration.toDouble/1000)+"sec.")
      if(result.atomicConcepts.exists(!signature(_)))
	println("not in signature: "+result.atomicConcepts.filter(forget).mkString(", "))
    } catch { 
      case _: TimeoutException => 
	    println("Neg Timeout.")
      exec.lastThread.getOrElse(throw new RuntimeException("Not started")).stop()
    }
    println()
    println()
  }

  def ontology(dlStatement: DLStatement) =
    Ontology.buildFrom(Set(dlStatement))

  def extractRandomConceptExpression(ontology: Ontology) = { 
    val axioms = ontology.tbox.axioms.toSeq

    val axiom = axioms(Random.nextInt(axioms.size))

    axiom match { 
      case Subsumption(_, c) => (c, axiom)
      case ConceptEquivalence(_, c) => (c, axiom)
    }
  }

  
  def main(args: Array[String]): Unit = { 
    print("Loading ontology...")
    val ontology = OWLApiInterface.getOWLOntology(new File(args(0)))
    OWLOntologyFilters.restrictToALCH(ontology)
    OWLOntologyFilters.clearABox(ontology)

    if(!OWLReasonerFacade.isConsistent(ontology))
       return 

    val restricted = ModuleExtractor.prepare(ontology)

    if(restricted.getLogicalAxioms().size<50){
      println("Too small")
      return
    }


    val repeat = args(1).toInt
    val timeout = args(2).toInt
        
    println("Running sequence of "+repeat+" runs, timeout: "+timeout)
    println("Ontology: "+ontology)

    for(i <- 1 to repeat){ 
      println("Experiment "+i)
      println("-----------------")
      
      experimentalRun(ontology, timeout seconds)
    }
 
  }


}
