/*
   Copyright 2010 Aaron J. Radke

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package cc.drx

//Log Level/Logger type class
trait LogLevel{
  def log(kvs:List[(String,Any)], srcLoc:File.Loc):Unit =
      require(skip == false, "The Log macro should prevent this method from being called if the skip is set")

  private val nullFile = File.Loc(File("/dev/null"),0)
  def apply(kvs:Tuple2[Any,Any]*):Unit = {
    val kvStrings = kvs.toList.map{
      case (Symbol(k),v) => k.underscore -> v
      case (k,v) => k.toString.underscore -> v
    }
    log(kvStrings, nullFile) //use the generic Log without a srcLoc
  }
  /**if this value is set then the macro should fire a skip for this event whenvernever even expand or evaluate the logging values */
  def skip:Boolean = true
}
/**a selection of implicit log levels, these can be used with import LogLevel.Debug to set a scoped debug level */
object LogLevel {
  /**show timestamp, feature values, and line numbers*/
  case object Debug extends LogLevel {
    override def skip = false
    override def log(kvs:List[(String,Any)], srcLoc:File.Loc):Unit =
      Console.println(
        Date.now.krypton.toString + " " + (kvs ++
          List("line" -> srcLoc.line, "file" -> srcLoc.file.unixPath)
        ).map(Format.kvColor).mkString(" ")
      )
  }
  /**just show feature values*/
  case object Info extends LogLevel{
    override def skip = false
    override def log(kvs:List[(String,Any)], srcLoc:File.Loc):Unit =
      Console.println( kvs.map(Format.kvColor).mkString(" ") )
  }
  /**don't show anything*/
  case object None extends LogLevel

  /**rate limited progress like info with ansi overwrite*/
  case object Progress extends LogLevel{    //--constants
    val dt = 100.ms
    private val ticker = new Ticker(dt)
    override def skip = {ticker.tick; ticker.count != 0}
    override def log(kvs:List[(String,Any)], srcLoc:File.Loc):Unit = {
      val rateKvs = kvs :+ ("logRate" -> ticker.meanRate)
       Ansi.printtmp( rateKvs.map(Format.kvColor).mkString(" ") )
    }

  }

  private class Ticker(dt:Time=100.ms){
    //--constants
    val dtNanos = dt.ns.toLong
  //--mutables
    private var lastTickTime:Long = System.nanoTime
    var count:Long = 0L  //number of skip tests per dt
    val rateStat:StatBuilder = new StatBuilder

    private def nanosSinceLast = (System.nanoTime - lastTickTime)

    def tick:Unit = {
      count += 1
      val ns = nanosSinceLast
      if(ns >= dtNanos){
        //--calculate tick rates
        val rate = count*1E9/ns
        rateStat += rate
        //--reset counters
        lastTickTime = System.nanoTime
        count = 0
      }
    }

    def meanRate:Frequency = rateStat.mean.hz
    def lastRate:Frequency = rateStat.last.hz
  }

  /**rate limited progress like info with ansi overwrite*/
  /*
  case object Top extends LogLevel{
    private val di = collections.mutable.Map[File.Loc,Ticker].empty.withDefault(0L)
    //TODO use a hash to store separate log times keying off of srcLoc (file & name)
    //TODO use the set of of active logs for a `top` like dashboard
    override def skip = ???
    override def log(kvs:List[(String,Any)], srcLoc:File.Loc):Unit = ???
  }
  */

  implicit val defaultDrxLogLevel:LogLevel = LogLevel.Debug
}