#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
#  Copyright (c) 2001 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:  1 September 2001
#

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
    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 "AbortAnim $no"
    wm title $w "GPS Manager: $TXT(animation)"
    wm transient $w
    wm geometry $w +$EPOSX+$EPOSY

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

    set AnimJobs($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"
    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
    pack $w.fr.frb.go $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 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" {
	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

    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 tps [lindex $args 0] ; set datum [lindex $args 1]
	    set AnimJobs($no) [after 0 [list AnimNextTR $no $tps $datum]]
	}
	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 tps datum} {
    # show next leg of track
    global AnimState AnimJobs AnimLook AnimElapsed UNDEFDATESECS

    switch $AnimState($no) {
	abort {
	    return
	}
	pause {
	    set AnimJobs($no) \
		    [after 500 [list AnimNextTR $no $tps $datum]]
	    return
	}
    }
    set AnimJobs($no) ""
    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 } {
	    set tint [expr $tm1-$tm0]
	    if { $AnimElapsed($no) != -1 } {
		incr AnimElapsed($no) $tint
	    }
	} else {
	    set tint 30 ; set AnimElapsed($no) -1
	}
	set d [AnimDelay $no $tint]
	set AnimJobs($no) \
		[after $d [list AnimNextTR $no [lreplace $tps 0 0] $datum]]
    } elseif { "$AnimState($no)" != "abort" } {
	set AnimState($no) init
    }
    return
}

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

    if { $AnimSpeed($no) > -1 } {
	set d [expr int($secs*1000.0/(1+$AnimSpeed($no)))]
    } else {
	set d [expr int($secs*1000*-($AnimSpeed($no)-1))]
    }
    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
	if { $nv > 0 } {
	    set v "x [expr $nv+1]"
	} elseif { $nv == 0 } {
	    set v $TXT(actual)
	} else { set v "/ [expr -$nv+1]" }
	set t "$TXT(speed): $v"
    }
    $w.fr.frs.sp configure -label $t
    return
}

