###############################################################################
#
# compat.eagle --
#
# Extensible Adaptable Generalized Logic Engine (Eagle)
# Eagle [Native Tcl] Compatibility Package File
#
# Copyright (c) 2007-2012 by Joe Mistachkin.  All rights reserved.
#
# See the file "license.terms" for information on usage and redistribution of
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id: $
#
###############################################################################

#
# NOTE: Use our own namespace here because even though we do not directly
#       support namespaces ourselves, we do not want to pollute the global
#       namespace if this script actually ends up being evaluated in Tcl.
#
namespace eval ::Eagle {
  #
  # NOTE: This procedure attempts to query the size from the host, in terms
  #       of columns and rows; failing that, it returns a reasonable default
  #       value.  Generally, this procedure is intended to be used only by
  #       the other procedures in this file.
  #
  proc getHostSize {} {
    if {[catch {host size} result] == 0} then {
      return $result
    }

    return [list 80 25]; # TODO: Good default?
  }

  #
  # NOTE: This procedure emulates the behavior of the native Tcl [parray]
  #       procedure: it prints (to stdout) the names and values contained
  #       in the specified array, or a subset of those names based on the
  #       specified pattern.
  #
  proc parray { a args } {
    if {[llength $args] > 2} then {
      error "wrong # args: should be \"parray a ?pattern?\""
    }

    upvar 1 $a array

    if {![array exists array]} {
      error [appendArgs \" $a "\" isn't an array"]
    }

    set names [lsort [eval array names array $args]]
    set maxLength 0

    foreach name $names {
      set length [string length $name]

      if {$length > $maxLength} {
        set maxLength $length
      }
    }

    set stringMap [list \b " " \t " " \r \xB6 \n \xB6]
    set maxLength [expr {$maxLength + [string length $a] + 2}]
    set hostLength [lindex [getHostSize] 0]
    set valueLength [expr {$hostLength - $maxLength - 5}]; # " ... "

    foreach name $names {
      #
      # NOTE: Format the array element name for display.
      #
      set nameString [string map $stringMap [appendArgs $a ( $name )]]

      #
      # NOTE: If the value by itself is too long to fit on one host line,
      #       just truncate and ellipsis it.
      #
      set valueString [string map $stringMap $array($name)]

      if {[string length $valueString] > $valueLength} then {
        set valueString [appendArgs [string range $valueString 0 \
            [expr {$valueLength - 4}]] " ..."]
      }

      #
      # HACK: Mono does not currently support calling the String.Format
      #       overload that takes a variable number of arguments via
      #       reflection (Mono bug #636939).
      #
      if {![isMono]} then {
        set line [string format -verbatim -- [appendArgs "\{0,-" \
            $maxLength "\} = {1}"] $nameString $valueString]
      } elseif {[llength [info commands object]] > 0} then {
        set line [object invoke String Format [appendArgs "\{0,-" \
            $maxLength "\} = {1}"] $nameString $valueString]
      } else {
        set line [format [appendArgs "%-" $maxLength "s = %s"] \
            $nameString $valueString]
      }

      puts stdout $line
    }
  }

  #
  # NOTE: This procedure emulates the behavior of the native Tcl [parray]
  #       procedure: it prints (to stdout) the names and values contained
  #       in the specified dictionary, or a subset of those names based on
  #       the specified pattern.
  #
  proc pdict { d } {
    set maxLength 0

    foreach {name value} $d {
      set length [string length $name]

      if {$length > $maxLength} {
        set maxLength $length
      }
    }

    set hostLength [lindex [getHostSize] 0]
    set valueLength [expr {$hostLength - $maxLength - 5}]; # " ... "

    foreach {name value} $d {
      #
      # NOTE: If the value by itself is too long to fit on one host line,
      #       just truncate and ellipsis it.
      #
      set valueString $value

      if {[string length $valueString] > $valueLength} then {
        set valueString [appendArgs [string range $valueString 0 \
            [expr {$valueLength - 4}]] " ..."]
      }

      #
      # HACK: Mono does not currently support calling the String.Format
      #       overload that takes a variable number of arguments via
      #       reflection (Mono bug #636939).
      #
      if {![isMono]} then {
        set line [string format -verbatim -- [appendArgs "\{0,-" \
            $maxLength "\} = {1}"] $name $valueString]
      } elseif {[llength [info commands object]] > 0} then {
        set line [object invoke String Format [appendArgs "\{0,-" \
            $maxLength "\} = {1}"] $name $valueString]
      } else {
        set line [format [appendArgs "%-" $maxLength "s = %s"] \
            $name $valueString]
      }

      puts stdout $line
    }
  }

  #
  # NOTE: This procedure emulates the behavior of the native Tcl [test]
  #       command as provided by its bundled "tcltest" package.  It is
  #       designed to automatically detect and handle the type of test
  #       specified by the various arguments, which may be an old style
  #       test or a new style test.
  #
  proc test { name description args } {
    #
    # NOTE: Determine if the caller is trying to run an old style or
    #       new style test and use the appropriate command.
    #
    if {[string index [lindex $args 0] 0] eq "-"} then {
      #
      # NOTE: New style test, use [test2] command.
      #
      set command test2
    } else {
      #
      # NOTE: Old style test, use [test1] command.
      #
      set command test1
    }

    return [uplevel 1 [list $command $name $description] $args]
  }

  #
  # NOTE: This procedure emulates the behavior of the native Tcl [tclLog]
  #       command.
  #
  proc tclLog { string } {
    #
    # NOTE: This should work properly in both Tcl and Eagle.
    #
    catch {puts stderr $string}
  }

  #
  # NOTE: Provide the Eagle "Tcl compatibility" package to the interpreter.
  #
  package provide Eagle.Tcl.Compatibility \
    [expr {[isEagle] ? [info engine PatchLevel] : "1.0"}]
}
