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

import java.io.File

import scala.io.Source

object Statistics { 

  var array: Seq[Seq[String]] = List()
  var keys: Seq[String] = List()

  def main(args: Array[String]) = { 
    val filename = args(0)
    statistics(filename)
  }

  def statistics(filename: String, 
		 byOntology: Boolean = false, 
		 nominals: Boolean = true,
	         oneLine: Boolean = false) = { 
    array = parseCSV(filename)

    keys = array.head
    array = array.tail



//    val signatureSizes = Seq(5,10,50,150)
    val differentiate = if(byOntology) "Ontology" else "Signature-Size"
    val parts = diffValues(array, differentiate).toSeq.sorted

//     println("Average axioms: "+average(values(array, "Ontology-Axioms")))
// //    val nonZero = filter(array, "Ontology-Axioms", _!="0")
//     println("Average axiom size: "+average(combine(values(array, "Ontology-size"),
// 						   values(array, "Ontology-Axioms"),
// 						   (a,b) => if(a==b)1 else a/(b+1))))
					   

    if(!oneLine){ 
    println("""\begin{center}
 \begin{tabular}{|c|c|c|c|c|c|c|c|}
  \hline""")
    }

    if(!oneLine)
      headerABox(differentiate, nominals)

    parts.foreach{ part => 
      print(part.take(10))
      var portion = filter(array, differentiate, _==part)//x => Range(sigSize-4,sigSize+3).contains(x.toInt))
      processPortionABox(portion, nominals)
    }


    if(!oneLine){ 
      var portion = array 

      print("All ")
      processPortionABox(portion, nominals)

      println("""
	      \end{tabular}
	      \end{center}""")
    }
  }
  
  def header(differentiate: String): Unit = { 
    println(differentiate+" & Axioms & Ax. Size & Num. Rest. & Definers  & Timeouts & Duration \\\\")
    println("\\hline")
  }


  def processPortion(_portion: Seq[Seq[String]], nominals: Boolean): Unit = { 

    var timeouts = filter(_portion, "Completed?", _=="false")


    val timeoutsProp = timeouts.size.toDouble/_portion.size*100

    var portion = _portion.diff(timeouts)
      
    print(" & " + "%.2f" format average(values(portion, "Output-Axioms")))
    print(" & " + "%.2f" format average(combine(values(portion, "Output-Size"),
			   			values(portion, "Output-Axioms"),
			   			(a, b) => if (a==b)1 else a/b)))

//    print(" & " + "%.2f" format average(values(portion, "Output-Max-Axiom-Size")))

    print(" & %.2f" format average(values(portion, "Number-Restrictions")))

    var defs = filterNot(portion, "Definers", Set("0","-1"))
    print(" & %.2f\\%%"  format (defs.size.toDouble/portion.size*100))

    print(" & %.2f\\%%" format timeoutsProp)

    print(" & " + "%.1f sec." format average(values(portion, "Duration"))/1000)
    
      
    println("\\\\\n\\hline")
    
  }



  def headerABox(differentiate: String, nominals: Boolean): Unit = { 
    print(differentiate+" & Timeouts & Duration & ABox axioms & axiom size & TBox axioms & axiom size & Definers") 

    if(nominals)
      print(" & Nominals")
    else
      print(" & Disjunctions")

    print(" & Data")
    
    print(" &  Disjunctions Eliminated")

    println("\\\\")
    println("\\hline")
  }
    
  
  def processPortionABox(__portion: Seq[Seq[String]], nominals: Boolean): Unit = { 
  //			   _portion = _portion.slice(_portion.size-161, _portion.size-1)


    var _portion = __portion.take(350)

    var timeouts = filter(_portion, "Completed?", _=="false")


    print(" & %.2f\\%%" format (timeouts.size.toDouble/_portion.size*100))
    var portion = _portion.diff(timeouts)

    print(" & " + "%.1f sec." format average(values(portion, "Duration"))/1000)

      
    print(" & " + "%.2f" format average(values(portion, "Output-ABox-Assertions")))
    print(" & " + "%.2f" format average(combine(values(portion, "Output-ABox-Size"),
    			   			values(portion, "Output-ABox-Assertions"),
    			   			(a, b) => if (a==b)1 else a/b)))

    print(" & " + "%.2f" format average(values(portion, "Output-Axioms")))

    val a = combine(values(portion, "Output-Size"),
		    values(portion, "Output-ABox-Size"),
		    (a,b) => a-b)

    print(" & " + "%.2f" format average(combine(a,
			   			values(portion, "Output-Axioms"),
			   			(a, b) => if (b==0) 0 else a/b)))

    var defs = filterNot(portion, "Definers", Set("0","-1"))
    print(" & %.2f\\%%"  format (defs.size.toDouble/portion.size*100))


    if(nominals){ 
      var noms = filter(portion, "Nominals", _!="0")
      print(" & %2f\\%%" format (noms.size.toDouble/portion.size*100))
    } else { 
      var disjs= filter(portion, "Disjunctive-Concept-Assertions", _!="0")
      print(" & %.2f\\%%"  format (disjs.size.toDouble/portion.size*100))
    }

    print(" & "+__portion.size)
    
    print(" & "+filter(portion, "Disjunctive-Eliminated?", _=="true").size/portion.size)

    println("\\\\\n\\hline")  
  }



  def diffValues(array: Seq[Seq[String]], key: String): Set[String] = { 
    values(array, key).toSet
  }

  def values(array: Seq[Seq[String]], key: String): Seq[String] = { 
    val column = keys.indexOf(key)

    array.map(_.apply(column))
  }

  def filter(array: Seq[Seq[String]], key: String, cond: String=>Boolean)
  : Seq[Seq[String]]= { 
    val column = keys.indexOf(key)

    array.filter(r => cond(r.apply(column)))
  }

  def filterNot(array: Seq[Seq[String]], key: String, cond: String=>Boolean)
  : Seq[Seq[String]]= { 
    val column = keys.indexOf(key)

    array.filterNot(r => cond(r.apply(column)))
  }

  def average(column: Iterable[String]): Double = { 
    val sum = column.map(_.toDouble).fold(0.0)((a,b) => a+b)
    sum.toDouble/column.size
  }

  def combine(column1: Seq[String], 
	      column2: Seq[String], 
	      func: (Double, Double)=>Double)
  : Seq[String]= { 
    (1 to column1.size-1).map(i => func.apply(column1(i).toDouble, column2(i).toDouble).toString)
  }

  def parseCSV(filename: String): Seq[Seq[String]] = { 
    Source.fromFile(new File(filename)).getLines.map(_.split("\t").toSeq).toSeq
  }
}
