#!/mod/bin/jimsh

source /mod/webif/lib/setup
require system.class settings.class ts.class rsv.class browse.class \
    queue.class \
    lock plugin safe_delete pretty_size

source /mod/webif/lib/auto/util.jim

set ::auto::settings [settings]
set ::auto::loglevel [$::auto::settings autolog]

set ::auto::force 0

while {[llength $argv]} {
	switch -- [lindex $argv 0] {
		-d {
			incr ::auto::loglevel
			set ::auto::logfd stdout
		}
		-f {
			set ::auto::force 1
		}
		-logprefix {
			set argv [lrange $argv 1 end]
			if {[llength $argv]} {
				set ::auto::logprefix [lindex $argv 0]
			}
		}
		default {
			# Pass to rest of script.
			break
		}
	}
	set argv [lrange $argv 1 end]
}

# Acquire lock
if {$::auto::logfd ne "unset"} {
	puts $::auto::logfd "Acquiring lock..."
}
if {![acquire_lock webif_autodeq]} {
	if {$::auto::loglevel > 1} {
		system plog auto "Could not acquire lock."
	}
	puts "Could not acquire exclusive lock, terminating."
	exit
}

::auto::loginit

######################################################################
# Determine if it's time to run

if {[system uptime] < 180} {
	::auto::log "Aborting, system has just booted." 2
	exit
}

::auto::dsc
::auto::oktorun

#########################################################################
# Initialisation

set scanstart [clock milliseconds]
::auto::log "Auto de-queue starting" 2

::auto::tmpdir "webif_autoq"

if {[system pkginst undelete]} {
	set ::auto::dustbin "[system dustbin]"
} else {
	set ::auto::dustbin ""
}

set ::auto::root [system mediaroot]
file stat "$::auto::root/" rootstat
set ::auto::rootdev $rootstat(dev)

#########################################################################
# Utility functions

######################################################################
# Plugin registration

set ::auto::plugins {}

proc ::auto::register {plugin {priority 500}} {
	variable plugins

	set fn "::${plugin}::dequeue"
	set plugins($plugin) $priority
	log "Registered $plugin with priority $priority" 2
}

######################################################################
# Load plugins

# Bundled
eval_plugins queue 1 "" /mod/webif/lib/auto/plugin

# Third party

eval_plugins queue 1

######################################################################
# Process the queue

queue startup [$::auto::settings autokeep]

proc ::auto::dumpq {qq} {
	foreach q $qq {
		set pri 0
		if {[$q get action] in $::auto::plugins} {
			set pri $::auto::plugins([$q get action])
		}
		log [format " C: %4d %5d %8s - [$q get file]" \
		    [$q get id] $pri [$q get action]] 2
	}
}

proc ::auto::runplugin {plugin fn args} {
	set rfn "::${plugin}::${fn}"
	if {![exists -proc $rfn]} { return -1 }
	if {[catch {set ret [uplevel 1 $rfn $args]} msg]} {
		log "$rfn: $msg" 0
		lassign [info stacktrace] p f l
		log "    $f:$l @ $p" 0
		return -1
	}
	return $ret
}

proc ::auto::runplugins {fn args} {
	foreach plugin [lsort -decreasing -command [lambda {a b} {
		if {$a ni $::auto::plugins} { return 0 }
		if {$b ni $::auto::plugins} { return 0 }
		return $($::auto::plugins($a) - $::auto::plugins($b))
	    }] [dict keys $::auto::plugins]] {
		set rfn "::${plugin}::${fn}"
		if {![exists -proc $rfn]} continue
		if {[catch {uplevel 1 $rfn {*}$args} msg]} {
			log "$rfn: $msg" 0
			lassign [info stacktrace] p f l
			log "    $f:$l @ $p" 0
		}
	}
}

# Helper function to sort a list of queue items by plugin priority
proc ::auto::pending {} {
	return [lsort -decreasing -command [lambda {a b} {
		set aa [$a get action]
		set ba [$b get action]
		if {$aa ni $::auto::plugins} { return 0 }
		if {$ba ni $::auto::plugins} { return 0 }
		set ap $::auto::plugins($aa)
		set bp $::auto::plugins($ba)
		if {$ap != $bp} { return $($ap - $bp) }
		return $([$b get id] - [$a get id])
	}] [queue pending]]
}

set ::auto::processed 0
for {set qq [::auto::pending]} {[llength $qq]} {set qq [::auto::pending]} {
	::auto::dumpq $qq

	incr ::auto::processed

	# Try to run the first item in the queue.
	set q [lindex $qq 0]
	set plugin [$q get action]
	set file [$q get file]

	::auto::log "De-queuing [$q get id] - [$q get action] - $file" 0

	if {[string match {file://*} $file]} {
		set arg [string range $file 7 end]
		if {![file exists $arg]} {
			::auto::log "file does not exist." 0
			$q update "FAILED" "File does not exist" 1
			continue
		}
	} elseif {[string first :// $file] != -1} {
		set arg $file
	} else {
		if {[catch {set arg [ts fetch $file]}] || $arg eq "0"} {
			::auto::log "ts load failed." 0
			$q update "FAILED" "Could not load .ts file" 1
			continue
		}
	}

	::auto::dsc
	::auto::oktorun

	$q update RUNNING "Started at [::auto::date]"

	set ologprefix $::auto::logprefix
	set ::auto::logprefix "$plugin:$::auto::logprefix"

	set st [clock milliseconds]
	lassign [::auto::runplugin $plugin dequeue $q $arg] code msg next
	set ::auto::logprefix $ologprefix
	set elapsed [::auto::elapsed $st]

	if {[string index $next 0] eq "+"} { incr next [clock seconds] }

	::auto::log "    $code - $msg - $next" 0
	switch -- $code {
	    "-1" {
		::auto::log "   Plugin failure" 0
		$q update "FAILED" "Plugin failure" 1
	    }
	    "OK" {
		$q update "COMPLETE" $msg 1 $elapsed
		::auto::runplugins dequeued $plugin $q $arg
	    }
	    "DEFER" {
		if {$next ne ""} { $q set start $next }
		$q update "DEFER" $msg 1 $elapsed
	    }
	    "FAILED" { $q update "FAILED" $msg 1 $elapsed }
	    default {
		$q update "FAILED" "Unknown response '$code'"
	    }
	}
}

######################################################################
# Cleanup

release_lock webif_autodeq

if {$::auto::processed || $::auto::loglevel > 1} {
	set q "s"
	if {$::auto::processed == 1} { set q "" }
	::auto::log "Auto de-queue processed $::auto::processed item$q in [\
	    ::auto::elapsed $scanstart] seconds."
}

