#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2004 Miguel Filgueiras (mig@ncc.up.pt) / Universidade do Porto
#
#    This program is free software; you can redistribute it and/or modify
#      it under the terms of the GNU General Public License as published by
#      the Free Software Foundation; either version 2 of the License, or
#      (at your option) any later version.
#
#      This program is distributed in the hope that it will be useful,
#      but WITHOUT ANY WARRANTY; without even the implied warranty of
#      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#      GNU General Public License for more details.
#
#      You should have received a copy of the GNU General Public License
#      along with this program.
#
#  File: mapanim.tcl
#  Last change:  29 November 2004
#

proc InitAnimation {type title args} {
    # create dialog window to control animation
    #  $type is in {TR, RealTimeLog}
    #  $title to display in dialog window
    #  $args will be passed to proc AnimStart
    # the following global vars are used:
    #  $AnimNo is a unique number for each animation
    #    the value -1 is used for RealTimeLog
    #  for animation number $no
    #   $AnimJobs($no) is a list of background jobs
    #   $AnimState($no) in {init, on, pause, abort}
    #   $AnimState($no,ext) is the corresponding external representation
    #   $AnimSpeed($no) is the speed factor -50..50
    #     for real time log, this sets RealTimeLogIntv in 1..101 seconds
    #   $AnimLook($no) is 1 if last point should be kept centred on the map
    #   $AnimElapsed($no) is number of seconds elapsed, or -1
    #   $AnimElapsed($no,ext) is the corresponding external representation
    #   $AnimData($no) is a pair with list of TPs remaining to be shown
    #     and the datum ($type==TR)
    global AnimNo AnimJobs AnimState AnimSpeed AnimLook AnimElapsed \
	    RealTimeLogIntv COLOUR EPOSX EPOSY TXT

    if { $type != "RealTimeLog" } {
	set no $AnimNo ; incr AnimNo
	set rtl 0
	set AnimSpeed($no) 0
    } else {
	set no -1 ; set rtl 1
	# this is to avoid the trace resetting RealTimeLogIntv to some
	#  unkown value when the scale is declared
	set AnimSpeed($no) [expr $RealTimeLogIntv-51]
    }

    # this name is used explicitly elsewhere
    set w .anim$no
    toplevel $w
    wm protocol $w WM_DELETE_WINDOW "AnimAbort $no"
    wm title $w "$TXT(animation)/GPS Manager"
    wm transient $w
    wm geometry $w +$EPOSX+$EPOSY

    trace variable AnimState($no) w AnimStateChange
    trace variable AnimSpeed($no) w AnimSpeedChange

    set AnimJobs($no) "" ; set AnimData($no) ""
    set AnimState($no) init ; set AnimState($no,ext) $TXT(animinit)
    set AnimLook($no) 1 ; set AnimElapsed($no) 0 ; set AnimElapsed($no,ext) ""

    frame $w.fr -borderwidth 5 -bg $COLOUR(selbg)
    label $w.fr.tit -text $title

    frame $w.fr.frs
    label $w.fr.frs.sl -text $TXT(slow) -width 6
    scale $w.fr.frs.sp -orient horizontal -from -50 -to 50  -showvalue 0 \
	    -width 8 -length 150 -variable AnimSpeed($no) -label xxx
    label $w.fr.frs.fst -text $TXT(fast) -width 6
    # must be here!
    if { $rtl } {
	set AnimSpeed($no) [expr $RealTimeLogIntv-51]
	set cs 2 ; set cf 0
    } else {
	set AnimSpeed($no) 0
	set cs 0 ; set cf 2
    }
    grid configure $w.fr.frs.sl -column $cs -row 0 -sticky news
    grid configure $w.fr.frs.sp -column 1 -row 0 -sticky news
    grid configure $w.fr.frs.fst -column $cf -row 0 -sticky news

    checkbutton $w.fr.look -text $TXT(centred) -variable AnimLook($no) \
	    -onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
    
    frame $w.fr.frst
    label $w.fr.frst.tit -text $TXT(state):
    label $w.fr.frst.st -width 10 -textvariable AnimState($no,ext)
    label $w.fr.frst.tm -width 15 -textvariable AnimElapsed($no,ext)

    frame $w.fr.frb
    button $w.fr.frb.go -text $TXT(start) \
	    -command "AnimStart $no $type $args ; \
	              $w.fr.frb.go configure -state normal"
    if { $type == "TR" } {
	button $w.fr.frb.skp -text $TXT(nextTP) \
	      -command "AnimSkipTR $no ; $w.fr.frb.skp configure -state normal"
    }
    button $w.fr.frb.hld -text $TXT(pause) \
	    -command "AnimPause $no ; $w.fr.frb.hld configure -state normal"
    button $w.fr.frb.abort -text $TXT(abort) \
	    -command "set AnimState($no) abort ; destroy $w ; AnimAbort $no"

    pack $w.fr.frst.tit $w.fr.frst.st $w.fr.frst.tm -side left
    if { $type != "TR" } {
	pack $w.fr.frb.go $w.fr.frb.hld $w.fr.frb.abort -side left
    } else {
	pack $w.fr.frb.go $w.fr.frb.skp $w.fr.frb.hld $w.fr.frb.abort \
		-side left
    }
    pack $w.fr.tit $w.fr.frs $w.fr.look $w.fr.frst $w.fr.frb -side top -pady 5
    pack $w.fr
    update
    RaiseWindow $w
    return
}

proc AnimPause {no} {
    # pause animation number $no
    global AnimState TXT RealTimeLogAnim

    switch $AnimState($no) {
	on { set AnimState($no) pause }
	pause {
	    if { $no == -1 && ! $RealTimeLogAnim } {
		bell
	    } else {
		set AnimState($no) on
	    }
	}
	default { bell }
    }
    return
}

proc AnimAbort {no} {
    # abort animation $no
    global AnimJobs AnimSpeed AnimLook AnimState AnimElapsed AnimData \
	    RealTimeLogAnim Map

    if { $no == -1 } { set RealTimeLogAnim 0 }
    foreach j $AnimJobs($no) { after cancel $j }
    trace vdelete AnimState($no) w AnimStateChange
    trace vdelete AnimSpeed($no) w AnimSpeedChange
    foreach v "AnimJobs AnimSpeed AnimLook AnimState AnimElapsed AnimData" {
	catch {unset $v($no)}
    }
    catch {unset AnimState($no,ext)}
    catch {unset AnimElapsed($no,ext)}
    $Map delete an=$no
    SetMapBounds
    return
}

proc AnimStart {no type args} {
    # start animation number $no
    #  $args is
    #   $type==TR: list of TPs (under TRTPoints format), and datum
    #   $type==RealTimeLog: date of first point in seconds
    global AnimState AnimElapsed Map RealTimeLogAnim AnimData

    switch $AnimState($no) {
	abort { return }
	pause {
	    if { $type == "RealTimeLog" && ! $RealTimeLogAnim } {
		bell
	    } else {
		set AnimState($no) on
	    }
	    return
	}
	on { bell ; return }
    }
    set AnimElapsed($no,ext) ""
    $Map delete an=$no
    SetMapBounds
    switch $type {
	TR {
	    set AnimElapsed($no) 0
	    set AnimData($no) $args
	    set AnimJobs($no) [after 0 [list AnimNextTR $no]]
	}
	RealTimeLog {
	    set AnimElapsed($no) $args
	}
    }
    set AnimState($no) on
    return
}

proc AnimNextRealTime {pos datum secs} {
    # show next point of real time track log
    #  $pos is list with latd, longd for $datum, at time $secs
    # must start animation if none is going on
    global AnimState AnimLook AnimElapsed RealTimeLogAnim TXT

    if { ([catch {set AnimState(-1)}] || $AnimState(-1) == "abort") && \
	    ! [winfo exists .anim-1] } {
	InitAnimation RealTimeLog $TXT(realtimelog)
	AnimStart -1 RealTimeLog $secs
	return
    }
    switch $AnimState(-1) {
	abort {
	    set RealTimeLogAnim 0
	    return
	}
	pause {
	    return
	}
    }
    set p [MapFromPosn [lindex $pos 0] [lindex $pos 1] $datum]
    PutMapAnimPoint $p -1 $AnimLook(-1)
    if { $AnimElapsed(-1) == -1 } {
	set AnimElapsed(-1,ext) ""
    } else {
	set t [expr $secs-$AnimElapsed(-1)]
	set AnimElapsed(-1,ext) "    [FormatTime $t]"
    }
    return
}

proc AnimStopRealTime {} {
    # real-time log has been stopped
    global AnimState

    set AnimState(-1) pause
    return
}

proc AnimNextTR {no} {
    # show next leg of track
    global AnimState AnimJobs AnimLook AnimElapsed AnimData UNDEFDATESECS

    switch $AnimState($no) {
	abort {
	    return
	}
	pause {
	    set AnimJobs($no) [after 500 [list AnimNextTR $no]]
	    return
	}
    }
    set AnimJobs($no) ""
    set tps [lindex $AnimData($no) 0] ; set datum [lindex $AnimData($no) 1]
    set tp0 [lindex $tps 0]
    set p [MapFromPosn [lindex $tp0 0] [lindex $tp0 1] $datum]
    PutMapAnimPoint $p $no $AnimLook($no)
    if { $AnimElapsed($no) == -1 } {
	set AnimElapsed($no,ext) ""
    } else { set AnimElapsed($no,ext) "    [FormatTime $AnimElapsed($no)]" }
    if { [set tp1 [lindex $tps 1]] != "" } {
	if { [set tm0 [lindex $tp0 5]] == $UNDEFDATESECS || \
		 [set tm1 [lindex $tp1 5]] == $UNDEFDATESECS || \
		 $tm1 < $tm0 } {
	    set tint 30 ; set AnimElapsed($no) -1
	} else {
	    set tint [expr $tm1-$tm0]
	    if { $AnimElapsed($no) != -1 } {
		incr AnimElapsed($no) $tint
	    }
	}
	set d [AnimDelay $no $tint]
	set AnimData($no) [list [lreplace $tps 0 0] $datum]
	set AnimJobs($no) [after $d [list AnimNextTR $no]]
    } elseif { $AnimState($no) != "abort" } {
	set AnimState($no) init
    }
    return
}

proc AnimSkipTR {no} {
    # skip to next TP
    global AnimState AnimJobs

    if { $AnimState($no) != "abort" } {
	foreach j $AnimJobs($no) {
	    after cancel $j
	}
	set AnimState($no) on
	set AnimJobs($no) [after 0 [list AnimNextTR $no]]
    }
    return
}    

proc AnimDelay {no secs} {
    # compute delay in msecs corresponding to $secs in real time
    global AnimSpeed

    set a 1.09211043408 ; set k 1.83131663025
    if { $AnimSpeed($no) > 0 } {
	set d [expr int($secs*1000.0/(pow($a,$AnimSpeed($no))*$k))]
    } elseif { $AnimSpeed($no) == 0 } {
	set d [expr int($secs*1000)]
    } else {
	set d [expr int($secs*1000*pow($a,$AnimSpeed($no))*$k)]
    }
    return $d
}

proc AnimStateChange {n no op} {
    # called by trace when $AnimState($no) has been changed
    global AnimState TXT

    set AnimState($no,ext) $TXT(anim$AnimState($no))
    return
}

proc AnimSpeedChange {n no op} {
    # called by trace when $AnimSpeed($no) has been changed
    # must call another proc with after to avoid segmentation fault

    after 0 "AnimSpeedChanged $no"
    return
}

proc AnimSpeedChanged {no} {
    global AnimState AnimSpeed RealTimeLogIntv TXT

    if { $AnimState($no) == "abort" || ! [winfo exists .anim$no] } { return }
    set w .anim$no
    set nv $AnimSpeed($no)
    if { $no == -1 } {
	# real time log interval
	set t "$TXT(rtimelogintv): [set RealTimeLogIntv [expr $nv+51]] s"
    } else {
	# animation speed
	set a 1.09211043408 ; set k 1.83131663025
	if { $nv > 0 } {
	    set v "x [format %.1f [expr pow($a,$nv)*$k]]"
	} elseif { $nv == 0 } {
	    set v $TXT(actual)
	} else {
	    set v "/ [format %.1f [expr pow($a,-$nv)*$k]]"
	}
	set t "$TXT(speed): $v"
    }
    $w.fr.frs.sp configure -label $t
    return
}

