#!/usr/bin/wish
wm title . "Snowstar 0.75.5"
wm minsize . 320 240 
wm resizable . 1 1
#
#Snow*, a Tk interface for the 'rio' tool from the SnowBlind Alliance
#and David Weekly
#
#Hacked together in bitter isolation on 2/14/99 by armadilo@ricochet.net
#See CHANGES files for a list of other contributors and recent
#updates. 
#
#
#Scrolled_Listbox, Scroll_Set and EventLog code lifted shamelessly
#from Brent Welch's "Practical Programming with Tcl/Tk" book. 
#

#
proc Scroll_Set {scrollbar geoCmd offset size} {
    if {$offset != 0.0 || $size != 1.0} {
	eval $geoCmd	; # Make sure it is visible
	$scrollbar set $offset $size
    } else {
	set manager [lindex $geoCmd 0]
	$manager forget $scrollbar; # hide it
    }
}


#
# Listbox with optional scrollbars.
#
proc Scrolled_Listbox { f args } {
    frame $f 
    listbox $f.list -selectmode extended\
	-xscrollcommand [list Scroll_Set $f.xscroll \
			     [list grid $f.xscroll -row 1 -column 0 -sticky we]] \
	-yscrollcommand [list Scroll_Set $f.yscroll \
			     [list grid $f.yscroll -row 0 -column 1 -sticky ns]] 
    eval {$f.list configure} $args
    scrollbar $f.xscroll -orient horizontal \
	-command [list $f.list xview]
    scrollbar $f.yscroll -orient vertical \
	-command [list $f.list yview]
    grid $f.list $f.yscroll -sticky news
    grid $f.xscroll -sticky news
    grid rowconfigure $f 0 -weight 1
    grid columnconfigure $f 0 -weight 1
    return $f.list
}
proc SnowstarBrowserUpdate { newpath } {
    global WorkingDir
    global hostlist
    global depth
    global dirlabel

    set newraw_dir [exec ls -1Fa $newpath ]
    $hostlist delete 0 end
    set depth 0
    set dirlist [split $newraw_dir \n] 
    foreach entry $dirlist {
    set newcooked_dir [string trimright $entry {\*} ]
            $hostlist insert end $newcooked_dir
	incr depth
    }
    $dirlabel config -text "pwd: $WorkingDir"
}

proc SnowstarRioUpdate { entry } {
    global riolist 
}
      
proc cdTo { path } {
    global WorkingDir
    global dst 
    #	puts $path
  if { [file isfile $path] < 1} {
 	   if { [file isdirectory $path] <1 } {
       		  set newpath [file readlink [string trimright $path {\@} ] ]
     	   } else {
              set newpath $path
           }
	      cd $newpath
	      set WorkingDir [pwd]
	      SnowstarBrowserUpdate $WorkingDir
  } else {
        rioCmd {upl} $path
         } 
}

proc newWinID {} {
    
    global winnum
    if {$winnum == 0 } {
	set winnum 1
    } else {
	incr winnum
    }    
}

proc getDst {} {
    
    global WorkingDir
    global e
    global t
    global winnum
    
    set path ""
    
    newWinID
    
    set t [toplevel .t$winnum ]
    set e_label [label $t.l -text "Enter path:" -anchor w]
    set e [entry $t.e -textvariable path -width 80 -relief sunken]
    pack $e_label
    pack $e
    bind $e <Return>  {setDst [$e get];destroy $t} 
}

proc setDst {newdst } {
    
    global dst
    global dstlabel
    
    set dst $newdst
    $dstlabel config -text "To:$dst"
    #puts "dst now set to:$dst"
}

proc getSrc {} {
    
    global WorkingDir
    global f
    global u
    global winnum 
    
    newWinID 
    set u [toplevel .u$winnum]
    set f_label [label $u.l -text "Enter path:" -anchor w] 
    set f [entry $u.f -textvariable path  -width 80 -relief sunken]
    pack $f_label
    pack $f 
    bind $f <Return> {SnowstarBrowserUpdate [$f get]; cdTo [$f get]; destroy $u}
}

proc setSrc { newsrc } {

     global from 
     global srclabel

     set from $newsrc
#     puts $from

	     $srclabel config -text "From:$from"
}

proc addEntry { } {
    global riolist 

    set selected_files [split [selection get] \n]
    foreach e $selected_files { $riolist insert end $e } 
  ##puts [$riolist index end]
}
 
proc dropEntry {widget} { 

      foreach j [$widget curselection] {$widget delete $j }
}

proc openTranscriptWindow { } {
    global input 
    global resbtn 
    global results 
    
    toplevel .t
    set results [text .t.results -width 80 -height 5 -yscrollcommand {.t.rsb set} ]
    scrollbar .t.rsb -command {.t.results yview} 
    set resbtn [button .t.resbtn -text "Busy" -state disabled -background\
		    red -command "destroy .t" ]
    
    pack .t.rsb -side right -fill y
    pack .t.results -side top -fill x 
    pack .t.resbtn -side top
}

proc rioDir { } {
    global input
    global riocmd
    global riolist

    set fcmd "/usr/local/bin/rio -d"
    set riocmd "dir"

    if { [winfo exists .t] == 0 } {
	openTranscriptWindow
    }
    .t.results delete 1.0 end
    
    $riolist delete 0 end 
    set elements " " 

    #Run the command through a pipe we can send to the transcript window. 
    if [catch {open "|$fcmd |& cat"} input ] {
	$results insert end $input
    } else {
	fileevent $input readable Log
    }
}	

proc rioSync { } {

    global synclist

    if [catch {open "|rio -g synclist.dat.gz"} cansync ] {
    	procErr "Could not find sync list on Rio" error
	puts $cansync
	} else {
		procErr "Got synclist" info
#		exec gzcat /tmp/synclist.dat.gz
		set curr_host [ exec hostname ]
	}
	
}

proc rioCmd { cmd ioh } {
    
    global dst
    global from
    global hostlist
    global input 
    global max
    global progmtr
    global results 
    global resbtn 
    global riocmd
    global riolist
    global t_progmtr 
    global WorkingDir
    global xfer_prog

#set up primitives for diag window

    if { [winfo exists .t] == 0 } {
	openTranscriptWindow
    }
    $results delete 1.0 end
    
    #puts "rioCmd received cmd as $cmd"     
    #puts "dst in rioCmd is:$dst"
    set riocmd $cmd
    
    switch $cmd {
	dir {
	    rioDir
	    return
	}
	upl {   
	    set num2upl [$hostlist curselection]
	    if  { [ llength $num2upl ] > 1 } { 
		set uplist [ open /tmp/uplist w 0666 ]
		foreach subscript $num2upl {
		    puts $uplist [string trim [$hostlist get $subscript]]
		}
		close $uplist
		$hostlist selection clear 0 end
		set fcmd "/usr/local/bin/rio -v -d -f /tmp/uplist" 
	    } else { 
		set song2upl [string trim [$hostlist get active]]
		set fcmd "/usr/local/bin/rio -v -d -u \"$song2upl\""
	    }
	    set t_progmtr [toplevel .t_mtr]
	    wm title .t_mtr "Upload Progress"
	    set progmtr [scale $t_progmtr.progmtr -variable xfer_prog -orient horizontal \
			     -label "Percent complete" -length 250  -from 0 -to 100 -tickinterval 20 -showvalue true]
	    pack $t_progmtr.progmtr -side top -fill both
	}

	dnl { 
	    
	    #puts "reached dnl in rioCmd"
	    set num2dnl [$riolist curselection]
	    if  { [ llength $num2dnl ] > 1 } { 
		#puts "multifile download"
		foreach subscript $num2dnl {		    
		    append fcmd "rio -v -g \"[string trim [$riolist get $subscript]]\" ; "
		}
		.debug.results insert end "fcmd: "
		.debug.results insert end $fcmd\n

	    } else {
		set item2get [string trim [$riolist get active]]
		set fcmd "rio -v -g \"$item2get\""
	    }
	    
	    set t_progmtr [toplevel .t_mtr]
	    wm title .t_mtr "Download Progress"
	    set progmtr [scale $t_progmtr.progmtr -variable xfer_prog -orient horizontal \
			     -label "Percent complete" -length 250  -from 0 -to 100 -tickinterval 20 -showvalue true]
	    pack $t_progmtr.progmtr -side top -fill both
	}
	
	init { 
	    set fcmd "/usr/local/bin/rio -d -i"
	}
	
	del {
	    set delsel [$riolist curselection]
	#    set filename [string trim $cursel] 
	    set fcmd ""
	    if { [llength $delsel] >1 } {
	    foreach item2del $delsel {
	    	    append fcmd " /usr/local/bin/rio -d -z \"[string trim [$riolist get $item2del ]]\" ; "
	    }
	    } else {
	    	    set fcmd "/usr/local/bin/rio -d -z \"[string trim [$riolist get $delsel ]]\" "
	    }
        }

	
	#This is just to make sure the GUI dst and internal dst match up. 
	
	cdTo $dst
	cd $dst
	set elements " "	
    }
    
    #We've got everything we need to know about the file and the op we're being
    #asked to perform
    #Now we write the actual command line to be run
    
    set command "$fcmd"
    #puts "command set to: $command"
    
    #Run the command through a pipe we can send to the transcript window. 
    
    set max 0
    if [catch {open "|$command |& cat"} input ] {
	$results insert end $input
    } else {
	fileevent $input readable Log
    }
    
    #After all the mucking about we've done with the filesystem and everything
    #it might be a good idea to refresh the directory browser contents
    SnowstarBrowserUpdate $WorkingDir 

    # It would be a better idea to refresh the rio listing, unfortunately 
    # there's no way to tell just how long you have to wait after performing
    # operation x before the rio is ready to be access. If you have get 
    # errors that say it can't open the device, either modify the 
    # pause length or comment out the automatic refresh completely. 
#    set pause_length 3000
#    after $pause_length rioCmd dir none
}

#This is the transcript generator. It takes input from the pipe we opened
#above as long as it keeps coming through.
#When the pipe closes, we change the button status from disabled to enabled

proc Log { } {
    global input 
    global results
    global resbtn
    global numsongs
    global t_progmtr
    
    if [eof $input] {
	catch {close $input}
	.t.resbtn configure -text "Dismiss" -state normal -background green \
	    -command "destroy .t"
	if { [winfo exists .t_mtr] > 0 } {
	    destroy .t_mtr
	}
    } else {
	gets $input line
	procLog $line
	.t.results insert end $line\n
	.t.results see end
    }
}

proc procLog { logline } {
    
    global memlabel
    global remlabel
    global usedlabel
    global tracklabel
    global riocmd
    global riolist
    global riosize
    global xfer_prog
    global progmtr
    global max
   
    set resetdir 0
    #set log [ $log_h dump -text 1.0 end ] 
    switch -regexp -- $logline {
	rx { 
		set powermsg "Rio Access Error:\nCannot read Directory\n(Check Power/Cable?) "		     	
		procErr $powermsg error
        }
	tx {
		set fullmsg "Rio Access Error:\n Not Enough Space\n"
		procErr $fullmsg error
	}

	entry { 
		set songs [lindex [split $logline] 4 ]
		$tracklabel config -text "No.  Tracks:\t\t$songs"
	}

        total {
		set totalmem [lindex [split $logline] 3 ]
		$memlabel config -text "Total Memory:\t\t$totalmem"
		$riolist delete 0 end 
		$riosize delete 0 end
	}

        {^unused } {
		set freemem [lindex [split $logline] 2 ]
		$remlabel config -text "Remaining Memory:\t$freemem"
        }
	
	used {
		set usedmem [lindex [split $logline] 4 ]
		$usedlabel config -text "Used Memory:\t\t$usedmem"
	}
        
	{[0-9][0-9]:[0-9][0-9]:[0-9][0-9]} {
		    set offset [string last {:} $logline ] 
		    set trackname [string range $logline [expr $offset+3] end]
		    
		    set tracksize [lindex [split $logline] 2 ]
		    $riolist insert end $trackname 
		    $riosize insert end $tracksize

	}
	
	block { 
	       set newmax [lindex [split $logline] 1]
	       set blk $newmax
	       if { $newmax < 10000 } {
	            if { $newmax > $max } {
	                set max $newmax
	            }
                    if { $blk > 0 } { 
		   	set blknum [expr 100 - 100 * $newmax / $max ]
		    	$progmtr set $blknum
	      	    } 
	      } else { 
	      	      set max 0
	      }
	}
    }
}

proc procErr { msg type } {
 
    global win_id

    incr win_id

    toplevel .$win_id
    set err_top .$win_id
    wm title .$win_id $type
    set err_parent [frame  $err_top.err_f ]
    label $err_parent.err_icon -bitmap $type
    label $err_parent.err_msg -text $msg
    button $err_parent.errbtn -text "OK"
    
    pack $err_parent.err_icon -side top
    pack $err_parent.err_msg -side top
    pack $err_parent.errbtn -side top
    pack $err_top.err_f
    $err_parent.errbtn configure -text "OK" -state normal -command \
	"destroy .$win_id"
}

#The mother of all initialization routines.
#This sets up and initializes everything

proc SnowstarFsBrowserInit { parent } {
    
    global src
    global WorkingDir
    global hostlist
    global hostsize
    global riolist
    global riosize
    global depth
    global from
    global dirlabel
    global dirframe
    global dst
    global win_id
   
    global memlabel
    global remlabel
    global usedlabel
    global tracklabel
    
    set WorkingDir [pwd]
    set contents [exec ls -1Fa $WorkingDir | sed -e s/\*//g]
    set depth 0 
    set src $WorkingDir
    set from $WorkingDir
    set win_id 0
    frame $parent

    
#set up directory browser stuff
    
    set dirframe [frame $parent.dirframe]
    set dirlabel [label $dirframe.dirlabel -width 30 -text \
    "Current:$WorkingDir" -relief groove -anchor w ]
     
    set hsizeframe [frame $parent.hsizeframe]
    set hszlabel [label $hsizeframe.hszlabel -width 10 -text \
    "Size:" -relief groove -anchor w ]

    set hostlist [Scrolled_Listbox $dirframe.hostlist -width 40 -height 25 ]
    set hostsize [Scrolled_Listbox $hsizeframe.hostsize -width 8 -height 25 ]    
#set up rio-side stuff 

    set arcframe [frame $parent.arcframe]
    set arclabel [label $arcframe.arclabel -width 40 -text "Rio Contents" \
		      -anchor w]
    set rsizeframe [frame $parent.rsizeframe ]
    set rsznull1 [label $rsizeframe.rsznull1 -width 10 -text "    \n\n\n\n" \
    -relief groove -anchor w ]
    set rszlabel [label $rsizeframe.rszlabel -width 10 -text "Size:" \
    -relief groove -anchor w ]

    set riolist [Scrolled_Listbox $arcframe.riolist -width 40 -height 25 ]
    set riosize [Scrolled_Listbox $rsizeframe.riosize -width 8 -height 25 ] 


    set memlabel [label $arcframe.memlabel -width 30 -text \
		      "Total memory:" -relief groove -anchor w]
    set remlabel [label $arcframe.remlabel -width 30 -text \
		      "Remaining memory:" -relief groove -anchor w]
    set usedlabel [label $arcframe.usedlabel -width 30 -text \
		       "Used memory:" -relief groove -anchor w]
    set tracklabel [label $arcframe.tracklabel -width 30 -text \
			"No. Tracks:" -relief groove -anchor w]

#set up command buttons here for upload,dir,delete,init,etc.
    
    set btnframe [frame $parent.btnframe -width 100 ]
#    set addbtn [button $btnframe.addbtn -text "Add to Upload list" \
#		    -command "addEntry" ]
    set delbtn [button $btnframe.delbtn -text "Delete from Rio" -command \
		    { rioCmd del none } ]
    set directorybtn [button $btnframe.directorybtn -text "  Rio Contents " \
			  -command { rioCmd dir none } ]
    set uploadbtn [button $btnframe.uploadbtn -text "Upload" -command \
		       { rioCmd upl $from } ]
    set dnloadbtn [button $btnframe.dnloadbtn -text "Download" -command \
		       { rioCmd dnl none } ]
    set initializebtn [button $btnframe.initializebtn -text "Init Rio" \
			   -command { rioCmd init none } ]
    set srcbtn [ menubutton $btnframe.srcbtn -text "Goto" -menu \
		      $btnframe.srcbtn.menu -relief raised]
    
    set sm [menu $btnframe.srcbtn.menu ]
    $sm add command -label path... -command { getSrc}
    
    set quitbtn [button $btnframe.quitbtn -text "Quit" -relief raised \
		     -command exit ]
    #pack GUI elements for sizing

    pack $dirframe.dirlabel -side top -fill x
    pack $dirframe.hostlist -side top -fill x

   # pack $hsizeframe.hszlabel -side top -fill x
   # pack $hsizeframe.hostsize -side top -fill x


    pack $arcframe.arclabel -side top -fill x
    pack $arcframe.memlabel -side top -fill x
    pack $arcframe.usedlabel -side top -fill x
    pack $arcframe.remlabel -side top -fill x
    pack $arcframe.tracklabel -side top -fill x
    pack $arcframe.riolist -side top -fill x

    pack $rsizeframe.rsznull1 -side top -fill x
    pack $rsizeframe.rszlabel -side top -fill x
    pack $rsizeframe.riosize -side top -fill x

    pack $btnframe.directorybtn -side top -fill x 
#   pack $btnframe.addbtn -side top -fill x 
    pack $btnframe.delbtn -side top -fill x 
    pack $btnframe.uploadbtn -side top -fill x 
    pack $btnframe.dnloadbtn -side top -fill x 
    pack $btnframe.initializebtn -side top -fill x 
    pack $btnframe.srcbtn -side top -fill x 
    pack $btnframe.quitbtn -side top -fill x

#pack frames

    pack $parent.dirframe -side left -expand true -anchor w   
   # pack $parent.hsizeframe -side left -expand true -anchor w
    pack $parent.btnframe -side left  -expand true -anchor n -pady 58
    pack $parent.arcframe -side left -expand true -anchor w   
    pack $parent.rsizeframe -side left -expand true -anchor w

    set dirlist [split $contents \n]
    foreach entry $dirlist {
        $hostlist insert end $entry
        incr depth
    }
    #puts $depth
    rioCmd dir blah
    #rioSync 
    bind $hostlist <Double-1> {foreach i [selection get] { cdTo $i } } 
    bind $riolist <Double-1>  {dropEntry $riolist }
}

#Catalysts
proc SnowstarWinSetup {} {

    global WorkingDir
    
    SnowstarFsBrowserInit .f 
    pack .f -expand true -fill both
}

global riocmd
global max

SnowstarWinSetup
