# -*- tcl -*-
#
# This bit of tcl contains almost all of the inotify setup and use
# code for filerunner.  The code that sets the watch is not included
# since it is short and needs to be called each time a new directory
# is set up. This code supports both linux 'inotify' and windows using
# the 'twapi::begin_filesystem_monitor'.


namespace eval inotify {
  variable dirs {}
}


proc setUpInotify {} {
  global glob config
  # We attempt to use the inotify thingy....
  # This could be complicated, but we will keep it simple
  # We only watch the two dirs we are interested in
  # if we catch a notify, we will check and update both
  # We continue to depend on the update time to prune
  # unneeded updates....

  # The following command is located in the unix or windows package
  inotifySetUp
  set glob(notify,Available) 1
  set glob(inotify_flags) $config(inotify_flags)
  
  set glob(notify,watchname) notifywatch
  set glob(notify_id,left) [set glob(notify_id,right) ""]
  foreach lr {left right} {
    ClearCherryPicker $lr
    ClearWatch $lr $glob($lr,pwd)
  }
}

#
# The capture code is just for fun.  It is intended to set up hard links
# to newly created files in a subdir of the files creation dir.
# One use for this is to force a temp file to stay around so we can look
# at it at our leisure.
#
# We only monitor the dirs in the two dir panes so there is where we look...
proc setCapture { inst dir } {
  global glob
  if { $inst != "left" && $inst != "right" } {
    return "parm 1 must be 'left' or 'right'"
  }
  set glob(capture_pwd,$inst) $glob($inst,pwd)
  set glob(capture_dir,$inst) $dir
  if { ![file isdirectory $glob($inst,pwd)/$dir] } {
    return "$glob($inst,pwd)/$dir is not a directory"
  }
  defineCapture
}
proc defineCapture {} {
  proc capture { note } {
    global glob
    # If here we have a Creation notify event and we are supposed to hard
    # link the new file to one of the same name in a subdir specified above
    # The parameter here is the 'watch read' result.  We want the file name
    # (7) and the watchid (1) which will tell us which dir ...

    puts "[lindex $note 1] $glob(notify_id,left)"
    if { [lindex $note 1] == $glob(notify_id,left) } {
      set inst left
    } else {
      set inst right
    }
    puts "$inst $glob(capture_pwd,$inst) != $glob(notify,$inst)"
    if { $glob(capture_pwd,$inst) != $glob(notify,$inst) } {
      set glob(capture_dir,$inst) [set glob(capture_pwd,$inst) ""]
      if { $glob(capture_dir,[Opposite $inst]) == "" } {
	return 1
      } else {
	return
      }
    }
    set dir $glob(notify,right)
    # It all looks good
    puts "Trying file link -hard \
	       $glob(notify,$inst)/$glob(capture_dir,$inst)/[lindex $note 7] \
	       $glob(notify,$inst)/[lindex $note 7]"
    catch {file link -hard \
	       $glob(notify,$inst)/$glob(capture_dir,$inst)/[lindex $note 7] \
	       $glob(notify,$inst)/[lindex $note 7] } out
    if {$out == 0 } { 
      puts "success"
    } else {
      puts $out
    }
  }
}
proc capture { note } { }


proc doInotifyUpdate {found} {
  global glob config
  # We need an update, but we don't want to do them too quickly and
  # overload things.
  foreach inst {left right} {
    if { $glob(inotify_after,$inst) != {} } {continue}
    if {[set lr [expr {$inst in $found}]] || $glob(n_files,$inst) != {} } {
      set timeval [expr {[clock seconds] - \
			     $glob($inst,lasttime) - \
			     $config(inotify_rate_limit)}]
      if {$timeval >= 0 } {
	catch {after cancel $glob(inotify_after,$inst)}
	set glob(inotify_after,$inst) {}
	ForceNoticeUpdate $inst $lr
	#	puts "now $timeval"
      } else {
	#Too fast, slow things down a bit
	set glob(inotify_after,$inst) [after [expr {-$timeval *1000}] \
					   ForceNoticeUpdate $inst $lr]
	frputs timeval "[clock seconds] "
      }
    }
  }
}


proc ForceNoticeUpdate {inst allflag } {
  global glob
  catch "after cancel $glob(inotify_after,$inst)"
  set glob(inotify_after,$inst) {}
  #  if { $glob(inotify_after,$inst) != {} } { puts "is later $allflag [clock seconds]"}
  frputs allflag
  if {$allflag} {
    set glob($inst,lastmtime) 0
    ListUpdater
    set glob(n_files,$inst) {}
    return
  }
  cherryPickUpdate $inst
  set glob($inst,lasttime) [clock seconds]
}

# The cherryPickUpdate routine updates the files dir list without doing the 
# whole dir list thing.  To do this we need to find the file in the list box.
# Since the listing may be large and a changing file may continue to change,
# we will keep a cache of such files.

proc cherryPickUpdate {args} {
  global glob config cherrycount
  if {! $glob(enableautoupdate)} {   
    trace remove variable glob(enableautoupdate) write cherryPickUpdate
    trace add variable glob(enableautoupdate) write cherryPickUpdate
    frputs "iNotify Cherry pick update blocked ++++++++++++++++++ "
    return
  } else {
    #    puts "cherry pick update"
    trace remove variable glob(enableautoupdate) write cherryPickUpdate 
  }
  #  set glob(enableautoupdate) 0
  # this counter just keeps on counting as long as fr lives...
  incr cherrycount
  foreach inst {left right} {
    #puts "CP $inst"
    foreach file $glob(n_files,$inst) {
      #puts "CP $file"
      set index [lsearch -exact -index 0 $glob(n_file_cache,$inst) $file]
      if {$index != -1} {
	# Cache hit, is it a repeat of a file we already updated?
	if {$cherrycount == [lindex $glob(n_file_cache,$inst) $index 2]} {
	  # Yes, just push on...
	  #frputs "iNotify CherryPick repeat "
	  continue
	}
	# NO, change the 'cherrycount' so we will pass the above test if
	# this file is in the list more than once.
	# According to Murphy, we may have all these reset while we
	# are trying to update things.  If this falls apart, just get out.
	
	if { [catch "lreplace glob(n_file_cache,$inst)  $index $index \
	                [list $file \
		        [lindex $glob(n_file_cache,$inst) $index 1]\
		        $cherrycount]" ] } {
	  frputs "iNotify CP fall apart "
	  break
	}
	# Here we change the meaning of 'index' above it is the entry number
	# in the cache, after the following, 
	# it is the index in 'glob(l/r,lvfile)'
	lset glob(n_file_cache,$inst) $index 2 $cherrycount
	set index [lindex $glob(n_file_cache,$inst) $index 1]
      } else {
	set index [lsearch -exact -index 1 $glob($inst,filelist) $file]
	frputs file index
	if {$index == -1} {
	  # file not found in the master list for this side
	  break
	}
	lappend glob(n_file_cache,$inst) [list $file $index $cherrycount]
      }
      #	puts "cache $glob(n_file_cache,$inst)"
      # size mtime mode usergroup link nlink atime ctime
      
      frputs  file index
      if {[catch {file lstat "$glob(notify,$inst)/$file" statinfo} ]} continue
      frputs file sz statinfo(size)
      set size $statinfo(size) 
      set mode [GetStringFromMode $statinfo(mode)]
      set usergroup [GetUidGidString $glob(notify,$inst)/$file \
			 $statinfo(uid) $statinfo(gid)]
      set nlink $statinfo(nlink)
      set mtime $statinfo(mtime)
      set atime $statinfo(atime)
      set ctime $statinfo(ctime)
      set lr $inst
      if {$glob(notify,left) == $glob(notify,right)} {
	set lr 	[list {left} {right}]
      }
      foreach side  $lr {
	if {$glob(notify,$inst) != $glob($side,pwd) } {
	  frputs glob(notify,$inst) glob(notify,[Opposite $inst]) glob($lr,pwd)
	  continue
	}
	lassign [$glob(listbox,$side).file yview] ypos yypos
	foreach lbentry $config(ListBoxColumns,$side) {
	  set flist [lindex $lbentry 0]
	  if {$flist == "file" || $flist == "slink" } continue
	  
	  lset glob($inst,lv$flist) $index  [eval $glob(lbscript,$flist)]
	}
      }
    }
    set glob(n_files,$inst) {}
  }
  #  set glob(enableautoupdate) 1
}

