#
#  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: garmin_realtime.tcl
#  Last change:  26 June 2001
#

#### real time logging control

proc GPSRealTimeLogOnOff {} {
    # start or stop the real time log
    # change the button label accordingly
    global TXT RealTimeLogOn WConf

    if { $RealTimeLogOn } {
	StopRealTimeLog
	set txt $TXT(getlog)
    } else {
	if { ! [StartRealTimeLog 2] } { return }
	set txt $TXT(stop)
    }
    foreach w $WConf(realtime) type $WConf(realtimetype) {
	switch $type {
	    button {
		$w configure -text $txt
	    }
	    menuentry {
		[lindex $w 0] entryconfigure [lindex $w 1] -label $txt
	    }
	}
    }
    return 
}

proc StartRealTimeLog {interval} {
    # start real time logging with given $interval in seconds
    # assume it is currently off
    # return 0 on failure
    global GPSProtocol RealTimeLogIntv RealTimeLogOn RealTimeLogLast MESS MYGPS

    set RealTimeLogIntv $interval
    switch $GPSProtocol {
	garmin {
	    if { ! [GarminStartPVT] } { return 0 }
	}
	stext {
	    if { ! [StartSimpleText] } { return 0 }
	}
	simul {
	    SimulPVTOn
	}
	nmea {
	    if { "$MYGPS" == "Lowrance" } {
		NMEASetupWindow
	    } elseif { ! [GarminStartNMEA] } { return 0 }
	}
	lowrance {
	    NMEASetupWindow
	}
	default {
	    GMMessage $MESS(rltmnotsupp)
	    return 0
	}
    }
    set RealTimeLogLast 0
    set RealTimeLogOn 1
    return 1
}

proc StopRealTimeLog {} {
    # stop real time logging
    # assume it is currently on
    global GPSProtocol RealTimeLogOn RealTimeLogAnim MYGPS

    switch $GPSProtocol {
	garmin {
	    GarminStopPVT
	}
	nmea {
	    if { "$MYGPS" == "Lowrance" } {
		NMEAOff
	    } else { GarminStopNMEA }
	}
	stext {
	    StopSimpleText
	}
	simul {
	    SimulPVTOff
	}
    }
    if { $RealTimeLogAnim } {
	AnimStopRealTime
	set RealTimeLogAnim 0
    }
    set RealTimeLogOn 0
    return
}

##### real time log window and hook to animation procs

proc UsePVTData {data} {
    # use position, velocity and time data
    #  UTC time - as a list with y m d h mn s
    #  position - as a list with latd, longd (for datum WGS 84)
    #  pos fix  - in {error, _, 2D, 3D, 2D-diff, 3D-diff, GPS, DGPS, Auto,
    #                 simul}
    #  pos error - as a list with
    #              estimated position error EPE in meters
    #              estimated horizontal error EPH in meters
    #              estimated vertical error in meters
    #  altitude - signed number in meters
    #  velocity vector - as list with vlat vlong valt, in m/s
    #  bearing (track over ground, course made good, track made good) - degrees
    # any value may be "_" for non-available data
    # columns with empty first values will be collapsed
    global TXT MPOSX MPOSY COLOUR DateFormat \
	    RealTimeLogIntv RealTimeLogLast RealTimeLogAnim DATEWIDTH \
	    PVTLast PVTState PVTStateExt PVTElapsed PVTZero PVTPosns \
	    PVTSecs PVTInterval GPSProtocol MYGPS

    set date [lindex $data 0]
    if { [string first "_" $date] != -1 } {
	if { "$GPSProtocol" == "garmin" || \
		("$MYGPS" == "Garmin" && "$GPSProtocol" == "nmea") } {
	    Log "UPVTD> bad date: $date"
	}
	return
    }
    set date [eval UTCToLocalTime $date]
    set secs [eval DateToSecs $date]

    # this frame is used elsewhere
    set fx .pvt.fri.frtbx
    if { "$PVTState" == "on" && ! [winfo exists .pvt] } {
	set PVTInterval $RealTimeLogIntv
	set PVTLast 0 ; set PVTStateExt $TXT(animon)
	set PVTElapsed "" ; set PVTZero -1
	set PVTPosns "" ; set PVTSecs ""

	trace variable PVTState w PVTStateChange
	trace variable PVTInterval w PVTIntervalChange

	toplevel .pvt
	wm protocol .pvt WM_DELETE_WINDOW PVTCancel
	wm title .pvt "GPS Manager: $TXT(realtimelog)"
	set x [expr $MPOSX+10] ; set y [expr $MPOSY+80]
	wm geometry .pvt +$x+$y
	# bind .pvt <Key-Return> PVTCancel

	label .pvt.tit -text $TXT(realtimelog)

	frame .pvt.fri -borderwidth 5 -bg $COLOUR(messbg)

	# see above for initialization of $fx
	frame $fx
	# see below for creation of $fb and $show
	set fb .pvt.frs.frbt
	set show $fb.show ; set mn $show.m
	set col 0
	foreach b "n d lat long alt fix epe eph epv velx vely velz bg" \
		m "5 $DATEWIDTH 10 10 4 4 4 4 4 6 6 6 4" \
		t $TXT(PVTflds) {
	    button $fx.tit$b -width $m -text $t -font fixed \
		    -command [list CollapseColumn "$fx.tit$b $fx.bx$b" $col \
		                                  $t menubtentry $mn $show]
	    BalloonBindings $fx.tit$b hide
	    listbox $fx.bx$b -height 15 -width $m -relief flat \
		    -yscrollcommand "$fx.bscr set" \
		    -selectmode single -exportselection 0
	    bind $fx.bx$b <Button-1> {
		MultSelect [winfo parent %W] [%W nearest %y] \
			"bxn bxd bxlat bxlong bxalt bxfix bxepe bxeph bxepv \
			bxvelx bxvely bxvelz bxbg"
	    }
	    bind $fx.bx$b <Double-1> {
		set i [%W nearest %y] ; set n [%W get $i]
		set ww [winfo toplevel %W]
		MultSelect [winfo parent %W] $i \
			"bxn bxd bxlat bxlong bxalt bxfix bxepe bxeph bxepv \
			bxvelx bxvely bxvelz bxbg"
		if { "$n" != "" } {
		    MarkPoint PVT $ww $i
		}
	    }
	    grid configure $fx.tit$b -column $col -row 0 -sticky news
	    grid configure $fx.bx$b -column $col -row 1 -sticky news
	    incr col
	}
	set boxes [list $fx.bxd $fx.bxn $fx.bxlat $fx.bxlong \
		$fx.bxalt $fx.bxfix $fx.bxepe $fx.bxeph $fx.bxepv \
		$fx.bxvelx $fx.bxvely $fx.bxvelz $fx.bxbg]
	scrollbar $fx.bscr -command [list ScrollMany $boxes]
	Mscroll $boxes
	grid configure $fx.bscr -column $col -row 1 -sticky news

	frame .pvt.frs
	# $fb == .pvt.frs.frbt
	frame $fb
	button $fb.rest -text $TXT(restart) \
		-command "PVTRestart ; $fb.rest configure -state normal"
	button $fb.pause -text $TXT(pause) \
		-command "PVTPause ; $fb.pause configure -state normal"
	menubutton $show -text $TXT(show) -menu $show.m -state disabled
	menu $show.m
	set fb1 ${fb}1
	frame $fb1
	button $fb1.stan -text $TXT(animation) \
		-command "set RealTimeLogAnim 1 ; \
		$fb1.stan configure -state normal"
	button $fb1.totr -text $TXT(mkTR) \
		-command "PVTToTR ; $fb1.totr configure -state normal"
	button $fb1.save -text $TXT(save) \
		-command "SaveFile comp PVTData .pvt ; \
		$fb1.totr configure -state normal"

	frame .pvt.frs.int
	set fs .pvt.frs.int
	label $fs.sl -text $TXT(slow) -width 6
	scale $fs.sp -orient horizontal -from 1 -to 101  -showvalue 1 \
		-width 8 -length 150 -variable PVTInterval \
		-label "$TXT(rtimelogintv) (s):"
	label $fs.fst -text $TXT(fast) -width 6
	grid configure $fs.fst -column 0 -row 0 -sticky news
	grid configure $fs.sp -column 1 -row 0 -sticky news
	grid configure $fs.sl -column 2 -row 0 -sticky news

	frame .pvt.frs.st
	set ft .pvt.frs.st
	label $ft.tit -text $TXT(state):
	label $ft.st -width 10 -textvariable PVTStateExt
	label $ft.tm -width 15 -textvariable PVTElapsed

	button .pvt.cnc -text $TXT(cancel) -command PVTCancel

	pack $fb.rest $fb.pause $show -side top -pady 3 -fill x
	pack $fb1.stan $fb1.totr $fb1.save -side top -pady 3 -fill x
	pack $fx
	pack $fs $ft $fb $fb1 -side left -padx 10
	pack $ft.tit $ft.st $ft.tm -side left
	pack .pvt.tit .pvt.fri .pvt.frs .pvt.cnc -side top -pady 5
    }
    set PVTInterval $RealTimeLogIntv
    if { $secs-$RealTimeLogLast >= $RealTimeLogIntv } {
	set RealTimeLogLast $secs
	if { [string first "_" [set p [lindex $data 1]]] != -1 } {
	    set posn ""
	    set p "??? ???"
	} else {
	    if { $RealTimeLogAnim } {
		after 0 [list AnimNextRealTime $p "WGS 84" $secs]
	    }
	    set posn [eval CreatePos $p DMS latlong [list {WGS 84}]]
	    set p [lrange $posn 2 end]
	}
	if { "$PVTState" == "on" } {
	    if { $PVTZero < 0 } { set PVTZero $secs }
	    set PVTElapsed "    [FormatTime [expr $secs-$PVTZero]]"
	    lappend PVTSecs $secs
	    lappend PVTPosns $posn
	    set pe [lindex $data 3] ; set v [lindex $data 5]
	    set col 0
	    foreach b "n d lat long alt fix epe eph epv velx vely velz bg" \
		    v [list [format "%4d." [incr PVTLast]] \
		            [eval FormatDate $DateFormat $date] \
		            [lindex $p 0] [lindex $p 1] \
		            [PVTFormat [lindex $data 4] %7.1f] \
		            $TXT(posfix[lindex $data 2]) \
		            [PVTFormat [lindex $pe 0] %5.1f] \
		            [PVTFormat [lindex $pe 1] %5.1f] \
		            [PVTFormat [lindex $pe 2] %5.1f] \
		            [PVTFormat [lindex $v 0] %7.1f] \
		            [PVTFormat [lindex $v 1] %7.1f] \
		            [PVTFormat [lindex $v 2] %7.1f] \
			    [PVTFormat [lindex $data 6] %3.1f]] {
		if { [$fx.bx$b size] == 0 && "$v" == "" } {
		    CollapseColumn "$fx.tit$b $fx.bx$b" $col \
			    [lindex $TXT(PVTflds) $col] \
			    menubtentry .pvt.frs.frbt.show.m .pvt.frs.frbt.show
		}
		incr col
		$fx.bx$b insert end $v ; $fx.bx$b see end
	    }
	}
    }
    update
    return
}

proc PVTCancel {} {
    global PVTState

    set PVTState abort
    trace vdelete PVTState w PVTStateChange
    trace vdelete PVTInterval w PVTIntervalChange
    destroy .pvt
    return
}

proc PVTToTR {} {
    # make a TR from the PVT data
    global PVTSecs PVTPosns

    set fx .pvt.fri.frtbx
    set tpfs "latd longd latDMS longDMS date secs alt"
    set i 0 ; set tps ""
    foreach p $PVTPosns s $PVTSecs {
	if { "$p" != "" } {
	    lappend p [$fx.bxd get $i] $s [$fx.bxalt get $i]
	    lappend tps [FormData TP $tpfs $p]
	}
	incr i
    }
    if { "$tps" == "" } { bell ; return }
    set opts "create revert cancel"
    GMTrack -1 $opts [FormData TR "Datum TPoints" [list "WGS 84" $tps]]
    return
}

proc PVTRestart {} {
    # continue after a pause or restart logging
    # clear information in PVT window if restarting
    global RealTimeLogLast PVTSecs PVTPosns PVTLast PVTState PVTElapsed PVTZero

    if { "$PVTState" == "pause" } {
	set PVTState on
    } else {
	set PVTSecs "" ; set PVTPosns ""
	set RealTimeLogLast 0
	set PVTLast 0 ; set PVTState on ; set PVTElapsed "" ; set PVTZero -1
	set fx .pvt.fri.frtbx
	foreach b "n d lat long alt fix epe eph epv velx vely velz bg" {
	    $fx.bx$b delete 0 end
	}
    }
    return
}

proc PVTPause {} {
    # pause or continue after a pause
    global PVTState

    if { "$PVTState" == "pause" } {
	set PVTState on
    } else { set PVTState pause }
    return
}

proc PVTStateChange {n no op} {
    # called by trace when $PVTState has been changed
    global PVTState PVTStateExt TXT

    set PVTStateExt $TXT(anim$PVTState)
    return
}

proc PVTIntervalChange {n no op} {
    # called by trace when $PVTInterval has been changed
    # if there is an animation window for the log set AnimSpeed(-1)
    #  that has a trace on it; otherwise set RealTimeLogIntv directly
    global PVTInterval RealTimeLogIntv AnimState

    if { [winfo exists .anim-1] } {
	after 0 "set AnimSpeed(-1) [expr $PVTInterval-51]"
    } else {
	set RealTimeLogIntv $PVTInterval
    }
    return
}

proc PVTFormat {f fmt} {

    if { [string first "_" $f] != -1 } { return "" }
    return [format $fmt $f]
}
