/**
 * Strategies for logging 
 */

module util/log
imports 
  collection/tuple/cons
  collection/tuple/common
  collection/list/common
signature
  constructors
    Emergency : Severity
    Alert     : Severity
    Critical  : Severity
    Error     : Severity
    Warning   : Severity
    Notice    : Severity
    Info      : Severity
    Debug     : Severity
    Vomit     : Severity
    
strategies

  debug-area(s|msg) =
      log(|Debug(), <conc-strings> ("entering area ", msg))
    ; s
    ; log(|Debug(), <conc-strings> ("leaving area ",  msg))

  risky(s|msg) =
    risky(s|Error(), msg)

  risky(s|severity, msg) =
    restore(s, log(|severity, msg, <id>))

strategies

  log(|severity : Severity, msg, term) =
    if-log-severity(
      log(|severity, msg)
      ; where(
          <copy-char; log-puts> (<add> (<severity-string; string-length> severity, 3), ' ')
        ; <write-in-text-to-stream> (<log-stream>, term)
        ; <log-puts> "\n"
        )
    | severity
    )

  log(|severity : Severity, msg) =
    if-log-severity(
      where(
        <try(not(is-list) ; ![<id>])>msg 
      ; map(is-string <+ write-to-string) 
      ; <concat-strings ; log-puts> ["[ ", <log-src>, " | ",  <severity-string> severity, " ] " | <id> ]
      ; <log-puts> "\n"
      )
    | severity
    )

  if-log-severity(s | severity) =
    if-verbose(s | <verbose-level> severity)

strategies

  log-stream =
    <get-config> "log-stream"
    <+ <getenv> "STRATEGO_LOG"
       ; <fopen> (<id>, "a")
    <+ <stderr-stream> ()

  set-log-stream =
    <set-config> ("log-stream", <id>)

  log-puts =
    <fputs> (<id>, <log-stream> ())

  log-src =
    whoami; base-filename
    <+ !"identity crisis"

rules

  verbose-level : Emergency() -> -10
  verbose-level : Alert()     -> -5
  verbose-level : Critical()  -> -2
  verbose-level : Error()     -> 0
  verbose-level : Warning()   -> 1
  verbose-level : Info()      -> 1
  verbose-level : Notice()    -> 2
  verbose-level : Debug()     -> 4
  verbose-level : Vomit()     -> 10

  severity-string : Emergency() -> "emergency"
  severity-string : Alert()     -> "alert"
  severity-string : Critical()  -> "critical"
  severity-string : Error()     -> "error"
  severity-string : Info()      -> "info"
  severity-string : Warning()   -> "warning"
  severity-string : Notice()    -> "notice"
  severity-string : Debug()     -> "debug"
  severity-string : Vomit()     -> "vomit"
  
  string-to-level : "emergency" -> level
    where <verbose-level> Emergency() => level
    
  string-to-level : "alert"     -> level
    where <verbose-level> Alert() => level

  string-to-level : "critical"  -> level
    where <verbose-level> Critical() => level
    
  string-to-level : "error"     -> level
    where <verbose-level> Error() => level
    
  string-to-level : "warning"   -> level
    where <verbose-level> Warning() => level
    
  string-to-level : "notice"    -> level
    where <verbose-level> Notice() => level
    
  string-to-level : "info"      -> level
    where <verbose-level> Info() => level
    
  string-to-level : "debug"     -> level
    where <verbose-level> Debug() => level
    
  string-to-level : "vomit"     -> level
    where <verbose-level> Vomit() => level

strategies

  /**
   * Logs the specified message as critical and terminates the program
   * with exit code 1.
   *
   * @type a -> _
   */
  fatal-err(|msg : String) =
    log(|Critical(), msg, <id>); <exit> 1

  /**
   * Logs the specified message as an error.
   *
   * @type a -> a
   */
  err(|msg : String) =
    log(|Error(), msg, <id>)

  /**
   * Logs the specified message as a warning.
   *
   * @type a -> a
   */
  warn(|msg : String) =
    log(|Warning(), msg, <id>)

  /**
   * Logs the specified message as a notice.
   *
   * @type a -> a
   */
  notice(|msg : String) =
    log(|Notice(), msg, <id>)

  /**
   * Logs the specified message as a debug.
   *
   * @type a -> a
   */
  dbg(|msg : String) =
    log(|Debug(), msg, <id>)

strategies

  /**
   * Logs the specified message as critical and terminates the program
   * with exit code 1.
   *
   * @type a -> _
   */
  fatal-err-msg(|msg : String) =
    log(|Critical(), msg); <exit> 1

  /**
   * Logs the specified message as an error.
   *
   * @type a -> a
   */
  err-msg(|msg : String) =
    log(|Error(), msg)

  /**
   * Logs the specified message as a warning.
   *
   * @type a -> a
   */
  warn-msg(|msg : String) =
    log(|Warning(), msg)

  /**
   * Logs the specified message as a notice.
   *
   * @type a -> a
   */
  notice-msg(|msg : String) =
    log(|Notice(), msg)