###
# Installer actions built into tclsh and invoked
# if the first command line argument is "install"
###
if {[llength $argv] < 2} {
    exit 0
}
namespace eval ::practcl {}
###
# Installer tools
###
proc ::practcl::_readfile {file} {
    set fin [open $file r]
    try {
	fconfigure $fin -encoding utf-8 -eofchar \x1A
	return [read $fin]
    } finally {
	close $fin
    }
}

###
# Return true if the pkgindex file contains
# any statement other than "package ifneeded"
# and/or if any package ifneeded loads a DLL
###
proc ::practcl::_pkgindex_directory {path} {
    set buffer {}
    set pkgidxfile [file join $path pkgIndex.tcl]
    if {![file exists $pkgidxfile]} {
	# No pkgIndex file, read the source
	foreach file [glob -nocomplain $path/*.tm] {
	    set file [file normalize $file]
	    set fname [file rootname [file tail $file]]
	    ###
	    # We used to be able to ... Assume the package is correct in the filename
	    # No hunt for a "package provides"
	    ###
	    lassign [split $fname -] package version
	    ###
	    # Read the file, and override assumptions as needed
	    ###
	    set dat [_readfile $file]
	    # Look for a teapot style Package statement
	    foreach line [split $dat \n] {
		set line [string trim $line]
		if {![string match "# Package *" $line]} {
		    continue
		}
		lassign $line - - package version
		break
	    }
	    # Look for a package provide statement
	    foreach line [split $dat \n] {
		set line [string trim $line]
		if {![string match "package provide *" $line]} {
		    continue
		}
		lassign $line - - package version
		break
	    }
	    append buffer "package ifneeded $package $version \[list\
		    source \[file join \$dir [file tail $file]\]\]" \n
	}
	foreach file [glob -nocomplain $path/*.tcl] {
	    if { [file tail $file] eq "version_info.tcl" } {
		continue
	    }
	    set dat [_readfile $file]
	    if {![regexp "package provide" $dat]} {
		continue
	    }
	    set fname [file rootname [file tail $file]]
	    # Look for a package provide statement
	    foreach line [split $dat \n] {
		set line [string trim $line]
		if {![string match "package provide *" $line]} {
		    continue
		}
		lassign $line - - package version
		if {[string index $package 0] in "\$ \[ @"} {
		    continue
		}
		if {[string index $version 0] in "\$ \[ @"} {
		    continue
		}
		append buffer "package ifneeded $package $version \[list\
			source \[file join \$dir [file tail $file]\]\]" \n
		break
	    }
	}
	return $buffer
    }
    set dat [_readfile $pkgidxfile]
    set trace 0
    #if {[file tail $path] eq "tool"} {
    #    set trace 1
    #}
    set thisline {}
    foreach line [split $dat \n] {
	append thisline $line \n
	if {![info complete $thisline]} {
	    continue
	}
	set line [string trim $line]
	if {$line eq ""} {
	    set thisline {}
	    continue
	}
	if {[string match "#*" $line]} {
	    set thisline {}
	    continue
	}
	if {[regexp "if.*catch.*package.*Tcl.*return" $thisline]} {
	    if {$trace} {
		puts "[file dirname $pkgidxfile] Ignoring $thisline"
	    }
	    set thisline {}
	    continue
	}
	if {[regexp "if.*package.*vsatisfies.*package.*provide.*return" $thisline]} {
	    if {$trace} {
		puts "[file dirname $pkgidxfile] Ignoring $thisline"
	    }
	    set thisline {}
	    continue
	}
	if {![regexp "package.*ifneeded" $thisline]} {
	    # This package index contains arbitrary code
	    # source instead of trying to add it to the main
	    # package index
	    if {$trace} {
		puts "[file dirname $pkgidxfile] Arbitrary code $thisline"
	    }
	    return {source [file join $dir pkgIndex.tcl]}
	}
	append buffer $thisline \n
	set thisline {}
    }
    if {$trace} {
	puts [list [file dirname $pkgidxfile] $buffer]
    }
    return $buffer
}

proc ::practcl::_pkgindex_path_subdir {path} {
    set result {}
    foreach subpath [glob -nocomplain [file join $path *]] {
	if {[file isdirectory $subpath]} {
	    lappend result $subpath {*}[_pkgindex_path_subdir $subpath]
	}
    }
    return $result
}
###
# Index all paths given as though they will end up in the same
# virtual file system
###
proc ::practcl::pkgindex_path args {
    set stack {}
    append buffer {lappend ::PATHSTACK $dir} "\n"
    foreach base $args {
	set base [file normalize $base]
	set paths {}
	foreach dir [glob -nocomplain [file join $base *]] {
	    if {[file tail $dir] eq "teapot"} {
		continue
	    }
	    lappend paths $dir {*}[::practcl::_pkgindex_path_subdir $dir]
	}
	set i [string length $base]
	# Build a list of all of the paths
	if {[llength $paths]} {
	    foreach path $paths {
		if {$path eq $base} {
		    continue
		}
		set path_indexed($path) 0
	    }
	} else {
	    puts [list WARNING: NO PATHS FOUND IN $base]
	}
	set path_indexed($base) 1
	set path_indexed([file join $base boot tcl]) 1
	foreach teapath [glob -nocomplain [file join $base teapot *]] {
	    set pkg [file tail $teapath]
	    append buffer [list set pkg $pkg] "\n"
	    append buffer {set pkginstall [file join $::g(HOME) teapot $pkg]} "\n"
	    append buffer {if {![file exists $pkginstall]} {
    installDir [file join $dir teapot $pkg] $pkginstall
}} "\n"
	}
	foreach path $paths {
	    if {$path_indexed($path)} {
		continue
	    }
	    set thisdir [file_relative $base $path]
	    set idxbuf [::practcl::_pkgindex_directory $path]
	    if { $idxbuf ne "" } {
		incr path_indexed($path)
		append buffer "set dir \[set PKGDIR\
			\[file join \[lindex \$::PATHSTACK end\] $thisdir\]\]" "\n"
		append buffer [string map {$dir $PKGDIR} [string trimright $idxbuf]] "\n"
	    }
	}
    }
    append buffer {set dir [lindex $::PATHSTACK end]} "\n"
    append buffer {set ::PATHSTACK [lrange $::PATHSTACK 0 end-1]} "\n"
    return $buffer
}

###
# topic: 64319f4600fb63c82b2258d908f9d066
# description: Script to build the VFS file system
###
proc ::practcl::installDir {d1 d2} {
    puts [format {%*sCreating %s} [expr {4 * [info level]}] {} [file tail $d2]]
    file delete -force -- $d2
    file mkdir $d2

    foreach ftail [glob -directory $d1 -nocomplain -tails *] {
	set f [file join $d1 $ftail]
	if {[file isdirectory $f] && [string compare CVS $ftail]} {
	    installDir $f [file join $d2 $ftail]
	} elseif {[file isfile $f]} {
	    file copy -force $f [file join $d2 $ftail]
	    if {$::tcl_platform(platform) eq {unix}} {
		file attributes [file join $d2 $ftail] -permissions 0o644
	    } else {
		file attributes [file join $d2 $ftail] -readonly 1
	    }
	}
    }

    if {$::tcl_platform(platform) eq {unix}} {
	file attributes $d2 -permissions 0o755
    } else {
	file attributes $d2 -readonly 1
    }
}

proc ::practcl::copyDir {d1 d2 {toplevel 1}} {
    #if {$toplevel} {
    #    puts [list ::practcl::copyDir $d1 -> $d2]
    #}
    #file delete -force -- $d2
    file mkdir $d2

    foreach ftail [glob -directory $d1 -nocomplain -tails *] {
	set f [file join $d1 $ftail]
	if {[file isdirectory $f] && $ftail ne "CVS"} {
	    copyDir $f [file join $d2 $ftail] 0
	} elseif {[file isfile $f]} {
	    file copy -force $f [file join $d2 $ftail]
	}
    }
}

switch -glob [lindex $argv 1] {
    mkzip {
	zipfs mkzip {*}[lrange $argv 2 end]
    }
    mkimg {
	zipfs mkimg {*}[lrange $argv 2 end]
    }
    [a-z]* {
	::practcl::[lindex $argv 1] {*}[lrange $argv 2 end]
    }
    default {
	puts stderr "usage: $argv0 operation ..."
	exit 1
    }
}
exit 0
