###############################################################################
#
# getEagle.tcl --
#
# Extensible Adaptable Generalized Logic Engine (Eagle)
# Eagle Distribution File Downloader
#
# 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: $
#
###############################################################################

if {![package vsatisfies [package provide Tcl] 8.4]} then {
  error "need Tcl 8.4 or higher"
}

if {[catch {package present Eagle}] == 0} then {
  error "need native Tcl"
}

###############################################################################

namespace eval ::Eagle::Common::Tools {
  #############################################################################
  #***************************** TOOL PROCEDURES ******************************
  #############################################################################

  proc appendArgs { args } {
    eval append result $args
  }

  proc writeFile { fileName data } {
    #
    # NOTE: Attempt to write binary data to the specified file, replacing
    #       all its previous contents, if any.
    #
    set file [open $fileName {WRONLY CREAT TRUNC}]
    fconfigure $file -encoding binary -translation binary
    puts -nonewline $file $data
    close $file; return ""
  }

  proc pageProgress { channel milliseconds } {
    #
    # NOTE: Show that something is happening...
    #
    catch {puts -nonewline $channel .; flush $channel}

    #
    # NOTE: Make sure that we are scheduled to run again.
    #
    after $milliseconds [namespace code [list \
        pageProgress $channel $milliseconds]]
  }

  proc getPage { uri channel quiet args } {
    #
    # NOTE: This procedure requires the modern version of the HTTP package,
    #       which is typically included with the Tcl core distribution.
    #
    package require http 2.0

    #
    # NOTE: Unless the caller forbids it, display progress messages during
    #       the download.
    #
    if {!$quiet} then {
      pageProgress $channel 1000
    }

    #
    # NOTE: All downloads are handled synchronously, which is not ideal;
    #       however, it is simple.
    #
    set token [eval [list ::http::geturl $uri] $args]
    set data [::http::data $token]
    ::http::cleanup $token

    #
    # NOTE: If download progress messages were emitted, start a fresh line.
    #
    if {!$quiet} then {
      catch {puts $channel [appendArgs " " $uri]; flush $channel}
    }

    return $data
  }

  #############################################################################
  #********************** TOOL VARIABLE SETUP PROCEDURE ***********************
  #############################################################################

  proc setupToolVariables {} {
    ###########################################################################
    #***************************** TOOL VARIABLES *****************************
    ###########################################################################

    #
    # NOTE: Prevent progress messages from being displayed while downloading
    #       from the Eagle web site?  By default, this is disabled.  If this
    #       is enabled, this script may appear to "hang" because no progress
    #       messages will be displayed.
    #
    variable quiet; # DEFAULT: false

    if {![info exists quiet]} then {
      set quiet false
    }

    #
    # NOTE: The base URI for the Eagle distribution web site.  Since the HTTP
    #       package provided by Tcl does not support SSL out of the box, it
    #       cannot be used here.
    #
    variable baseUri; # DEFAULT: http://eagle.to/

    if {![info exists baseUri]} then {
      set baseUri http://eagle.to/
    }

    #
    # NOTE: The URI where the Eagle "update" file may be found.  This file will
    #       contain textual, line-oriented data describing the latest version
    #       available, on a per-vendor basis.
    #
    variable updateUri; # DEFAULT: ${baseUri}update.txt

    if {![info exists updateUri]} then {
      set updateUri [appendArgs $baseUri update.txt]
    }

    #
    # NOTE: The "root name" of the file name to be downloaded.  Normally, this
    #       will be the Eagle binary distribution; however, there are several
    #       other valid choices.
    #
    variable rootName; # DEFAULT: EagleBinary

    if {![info exists rootName]} then {
      set rootName EagleBinary
    }

    #
    # NOTE: The URI where the Eagle binary distribution may be found.  Setting
    #       this variable requires the patch level, which will not be known
    #       until later; therefore, the [subst] command will be used on the
    #       value of this variable at that point.
    #
    variable binaryUri; # DEFAULT: ${baseUri}/.../${rootName}${patchLevel}.exe

    if {![info exists binaryUri]} then {
      set binaryUri [appendArgs {${baseUri}} /releases/ {${patchLevel}} / \
          {${rootName}} {${patchLevel}} .exe]
    }

    #
    # NOTE: The regular expression pattern used to extract the patch level from
    #       the downloaded Eagle release data from "update.txt" (see above).
    #
    variable patchLevelPattern; # DEFAULT: ... {(\d+\.\d+\.\d+\.\d+)} ...

    if {![info exists patchLevelPattern]} then {
      set patchLevelPattern [appendArgs ^1 {\t} 29c6297630be05eb {\t} Eagle \
          {\t} invariant {\t} {(\d+\.\d+\.\d+\.\d+)} {\t}]
    }
  }

  #############################################################################
  #************************** TOOL STARTUP PROCEDURE **************************
  #############################################################################

  proc downloadEagle { directory fileName channel } {
    global env
    variable baseUri
    variable binaryUri
    variable patchLevelPattern
    variable quiet
    variable rootName
    variable updateUri

    #
    # NOTE: Attempt to fetch the latest Eagle release information.
    #
    set data [getPage $updateUri $channel $quiet]

    #
    # NOTE: Attempt to extract the patch level from the Eagle release
    #       information.  If this fails, the full URI for the Eagle
    #       binary distribution download cannot be determined.
    #
    set patchLevel [string trim [lindex [regexp -line -inline -- \
        $patchLevelPattern $data] end]]

    if {[string length $patchLevel] == 0} then {
      error "cannot determine Eagle patch level"
    }

    #
    # NOTE: Figure out the final Eagle binary distribution download URI,
    #       replacing any contained variable references as necessary using
    #       [subst].
    #
    set uri [subst -nobackslashes -nocommands $binaryUri]

    #
    # NOTE: Figure out where the downloaded Eagle binary distribution files
    #       are going to reside.
    #
    if {[string length $fileName] == 0} then {
      #
      # HACK: Using [file tail] to grab file name from URI.
      #
      set fileName [file join $env(TEMP) [file tail $uri]]
    }

    #
    # NOTE: Attempt to fetch the binary distribution file for Eagle.  This
    #       should be the latest available version.
    #
    set data [getPage $uri $channel $quiet -binary true]

    #
    # NOTE: Write the downloaded file data to the specified local file.
    #
    writeFile $fileName $data

    #
    # NOTE: Unless quiet mode is enabled, print the downloaded file name.
    #
    if {!$quiet} then {
      catch {puts $channel [appendArgs "file: " $fileName]; flush $channel}
    }

    #
    # NOTE: Return the name of the local file where the binary distribution
    #       file for the latest Eagle was saved.
    #
    return $fileName
  }

  #############################################################################
  #******************************* TOOL STARTUP *******************************
  #############################################################################

  #
  # NOTE: First, setup the script variables associated with this tool.
  #
  setupToolVariables

  #
  # NOTE: Next, save the tool path for later use.
  #
  if {![info exists toolPath]} then {
    set toolPath [file normalize [file dirname [info script]]]
  }

  #
  # NOTE: Finally, attempt to download the latest Eagle binary distribution
  #       file right now.
  #
  downloadEagle $toolPath [lindex $argv 0] stdout
}
