#
#  gpsman --- GPS Manager: a manager for GPS receiver data
#
# Copyright (c) 1998-2008 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: lowrance_patches.tcl
#  Last change:  27 September 2008
#
# this file contains patches to keep the Lowrance support code (in files
#  lowrance.tcl, lowrance_nmea.tcl, lowrance_symbols.tcl) compatible
#  with GPSMan after version 5.4.2
#

## possible sources of problems:
#   - proc StoreWP (know.tcl) can change the name of a WP; this may
#     have impact on the list of WP names of an incoming RT
#
#   - lowrance*.tcl not adequately protected against SRLFILE and SERIALPORT
#     being undefined
#

## symbols used by Magellan
#   from magellan.tcl Copyright (c) 2003 Matt Martin (matt.martin@ieee.org)
#   needed for importation/exportation of MapSend files (files_foreign.tcl)

set MAG_SYMTAB {WP_dot square_green house avn_vortac airport amusement_park
    casino car_repair boat camping exit 1st_aid avn_vordme buoy fuel deer
    golf lodging fish large_city light capitol_city boat_ramp medium_city
    museum danger park store knife_fork mountains diver_down_1 RV_park
    military scenic small_city oil_field stadium info truck_stop
    drinking_water wreck zoo}

## use of proc DestroyRGrabs (util.tcl) instead of the destroy command

proc CloseInProgrWindow {} {
    global ProgrWGrabs

    DestroyRGrabs .inprogr $ProgrWGrabs
    return
}

proc FailsInProgrWindow {mess} {
    # create dialog for signaling operation in progress
    # fail if window already exists
    #  single button: Abort; no bindings

    global COLOUR EPOSX EPOSY TXT ProgrWGrabs

    if { [winfo exists .inprogr] } { Raise .inprogr ; bell ; return 1 }
    set ProgrWGrabs [grab current]
    toplevel .inprogr
    wm protocol .inprogr WM_DELETE_WINDOW { bell }
    wm title .inprogr "GPS Manager: $TXT(commrec)"
    wm transient .inprogr
    wm geometry .inprogr +$EPOSX+$EPOSY

    frame .inprogr.fr -relief flat -borderwidth 5 -bg $COLOUR(messbg)
    label .inprogr.fr.title -text "..." -relief sunken
    label .inprogr.fr.text -text "$mess"
#    button .inprogr.fr.stop -text "STOP" -default active -command "NMEAOff"
    set fw .inprogr.fr.frc
    frame $fw -relief flat -borderwidth 0
    label $fw.tit -text "Working - please wait"
    button .inprogr.fr.ok -text "Stop" -state normal \
	-command NMEAOff
#    button .inprogr.fr.ok -text $TEXT(abort) -command NMEAOff
    pack $fw.tit -side left -padx 0
    pack .inprogr.fr -side top
    pack .inprogr.fr.title .inprogr.fr.text .inprogr.fr.frc \
	.inprogr.fr.ok -side top -pady 5
#    pack .inprogr.fr.stop -side top -pady 5
    update idletasks
    grab .inprogr.fr.ok
    RaiseWindow .inprogr
    return 0
}

## GPSMan RT identifiers can now be a string but Lowrance needs integers
proc SendData {command indx} {

    # transfer data to receiver
    # first group sent will be the Command Header. This will
    #  be followed by a data group for PUTS (& for some GETS) or will 
    #  trigger a reply header & data group for GETS.
    #  Second arg is the data index or zero, as appropriate. 

global WPName WPNum UnusedWPInx UnusedICInx RTWPoints RTList TRNumDeltas \
	PntCount NDeltas CMD CurrentTrack MESS RTIdNumber

    set data ""
    switch $command {
	GetWP -
	GetICpos - 
	GetRT -
	GetICgr {
	    set data [PacketHeader $CMD($command) 2]
	    lappend data [CheckSum $data]
	    set suffix [binary format "s" $indx]
	    set d [split $suffix ""]
	    lappend data [lindex $d 0] [lindex $d 1] 
	    lappend data [CheckSum $d]
	}    
	GetTR {
	    set data [PacketHeader $CMD($command) 1]
	    lappend data [CheckSum $data]
	    set suffix [binary format "c" $indx]
	    lappend data $suffix
	    lappend data [CheckSum $suffix]
	}
	GetTRd {
	    set deltaslice 20
	    set sdata [PacketHeader $CMD($command) 3]
	    lappend sdata [CheckSum $sdata]
	    set suffix [binary format "c" $indx]
	    if {$TRNumDeltas < $deltaslice} {
		set deltas [binary format "s" $TRNumDeltas]
		set TRNumDeltas 0
	    } else {
		set deltas [binary format "s" $deltaslice]
		set TRNumDeltas [expr ($TRNumDeltas - $deltaslice)]
	    }
	    set d [split $deltas ""]
	    lappend suffix [lindex $d 0] [lindex $d 1]
	    lappend suffix [CheckSum $suffix]
	    set data [concat $sdata $suffix]
	}
	PutWP {
	    if { $WPNum($indx) < 0} {
		set WPNum($indx) [lindex $UnusedWPInx 0]
		set UnusedWPInx [lreplace $UnusedWPInx 0 0]
	    }
	    set sdata [PacketHeader $CMD($command) 29]
	    lappend sdata [CheckSum $sdata]
	    set tvals [PrepData $command $indx]
	    set packdata [PackData $tvals]
	    lappend packdata [CheckSum $packdata]
	    set data [concat $sdata $packdata]
	}
	PutRT {
	    if { ! [regexp {^[0-9]+$} $RTIdNumber($ix)] } {
		GMMessage [format $MESS(cantsaveRTid) 1]
		return
	    }
	    set count [array size WPName]
	    set RTList ""
	    foreach pnt $RTWPoints($indx) {
		set inx [IndexNamed WP $pnt]
		if { ! [string compare WPNum($inx) "" ] } {
		    if { [llength $unusedWPInx] == 0 } {
			GMMessage $MESS(noWPinx)
			ResetCursor .
		    } else {
			set WPNum($inx) [lindex $UnusedWPInx 0]
			set UnusedWPInx [lreplace $UnusedWPInx 0 0]
		    }
		}
		lappend RTList $WPNum($inx)
	    }
	    set PntCount [llength $RTList]
	    set numbytes [expr ($PntCount * 2) + 16]
	    set data [PacketHeader $CMD($command) $numbytes]
	    lappend data [CheckSum $data]
	    set tvals [PrepData $command $indx]
	    set packdata [PackData $tvals]
	    set chk [CheckSum $packdata]
	    set packdata [concat $packdata [list $chk]]
	    set data [concat $data $packdata]
	}
	PutIC {
	    foreach i { $indx } {
		if { ! [string compare WPNum($i) "" ] } {
		    if { [llength $unusedICInx] == 0 } {
			GMMessage $MESS(noICinx)
			ResetCursor .
		    } else {
			set WPNum($i) [lindex $UnusedICInx 0]
			set UnusedICInx [lreplace $UnusedICInx 0 0]
		    }
		}
	    }
	    set count [llength $indx]
	    set data [PacketHeader $CMD(PutICno) 2]
	    lappend data [CheckSum $data]
	    set suffix [binary format "s" $count]
	    set d [split $suffix ""]
	    lappend data [lindex $d 0] [lindex $d 1] [CheckSum $d]
	    GPSInterface PutICno $data
	    for {set i 0} {$i < $count} {incr i} {
		set inx [lindex $indx $i]
 		set data [PacketHeader $CMD(PutIC) 11]
		lappend data [CheckSum $data]
		set tvals [PrepData $command $inx]
		set packdata [PackData $tvals]
		lappend packdata [CheckSum $packdata]
		set data [concat $data $packdata]
		GPSInterface $command $data
	    }
	    return
	}
	PutTR {
	    set CurrentTrack $indx
	    set sdata [PacketHeader $CMD($command) 11]
	    lappend sdata [CheckSum $sdata]
	    set tvals [PrepData $command $indx]
	    set packdata [PackData $tvals]
	    lappend packdata [CheckSum $packdata]
	    set data [concat $sdata $packdata]
	}
	PutTRd {
	    if {$PntCount < 40} {
		set NDeltas $PntCount
		set PntCount 0
	    } else {
		set NDeltas 40
		set PntCount [expr $PntCount - 40]
	    }
	    set n [expr $NDeltas * 4 + 3]
	    set data [PacketHeader $CMD($command) $n]
	    lappend data [CheckSum $data]
	    set tvals [PrepData $command $CurrentTrack]
	    set packdata [PackData $tvals]
	    lappend packdata [CheckSum $packdata]
	    set data [concat $data $packdata]
	}
	default {
	    set data [PacketHeader $CMD($command) 0]
	    lappend data [CheckSum $data]
	}
    }
    GPSInterface $command $data
    return
}

## change of proc PosType to array POSTYPE
## no need for position type in calls to proc CreatePos
## no need for SFilePFrmt

proc WriteHeader1 {file pformt fname} {
    # write header to file in GPSMan format, using $pformt for positions
    global TimeOffset Datum FCOMMAND CREATIONDATE \
	    SFilePFrmt SFileDatum SFileHeader MESS

    if {[file size $fname] ==0 } {
	puts $file "$FCOMMAND(comment) $MESS(written) GPSManager [NowTZ]"
	puts $file "$FCOMMAND(comment) $MESS(editrisk)"
	puts $file ""
	puts $file "$FCOMMAND(format) $pformt $TimeOffset $Datum"
	puts $file "$FCOMMAND(dates) $FCOMMAND($CREATIONDATE)"
	puts $file ""
    }
	set SFilePFrmt($file) $pformt
	set SFileDatum($file) $Datum
	set SFileHeader($file) 1
    }
    return
}

# use of FormatPosition instead of CreatePos
proc ConvWPData {d} {
    # convert WPs data got from receiver into list of lists suitable for
    #  use with SetItem
    global PositionFormat DEFAULTDISPOPT DATAFOR

    set wpnum [lindex $d 0]
    set wpname [lindex $d 4]
    set date [ConvLowrDate [lindex $d 5]]
    set s [lindex $d 1]
    set sym [NameForCodeOf SYMBOL $s]
    set latd [lindex $d 2] ; set longd [lindex $d 3]
    foreach "p pfmt datum" \
	[FormatPosition $latd $longd "WGS 84" $PositionFormat "" DDD] {
	    break
    }
    set all [list Name Num Posn Date Symbol Datum PFrmt]
    set r [list $wpname $wpnum $p $date $sym $datum $pfmt]
    set r [FormData WP $all $r]
    return $r
}

# use of FormatPosition instead of CreatePos
proc ConvICData {d} {
    # convert Icon data got from receiver into list of lists suitable for
    #  use with SetItem
    global PositionFormat DEFAULTDISPOPT

    set id [lindex $d 0]
    set name [lindex $d 4]
    set latd [lindex $d 1] ; set longd [lindex $d 2]
    foreach "p pfmt datum" \
	[FormatPosition $latd $longd "WGS 84" $PositionFormat "" DDD] {
	    break
    }
    set s [lindex $d 3]
    set sym [NameForCodeOf SYMBOL $s]
    set all [list Name Num Posn Symbol Datum PFrmt]
    set r [list $name $id $p $sym $datum $pfmt]
    set r [FormData WP $all $r]
    return $r
}

# replacing call to CreatePos by call to FormatLatLong
proc InDataTR {data} {
    # add TRs data from receiver to database

global Index Number GetDispl

    foreach p $data {
	set tps ""
	set f [lindex $p 0]
	set id [lindex  $f 0]
	set ix [IndexNamed TR $id]
	if {$ix == -1} then {
	    foreach d $p {
		set t [FormatLatLong [lindex $d 3] [lindex $d 4] DMS]
		lappend t [lindex $d 1] [lindex $d 2]
		lappend tps $t
	    }
	    set trdata [list [lindex $d 0] $tps]
	    set all [list Name TPoints]
	    set r [FormData TR $all $trdata]
	    StoreTR $ix $id $r $GetDispl
	}
    }
    return
}

# replacing call to CreatePos by call to FormatLatLong
proc NMEAInDataTR {id bulkdata} {
    # add TRs data from NMEA receiver to database

    global DataDefault NMEADatum Index Number GetDispl

    set DataDefault(TR) [list "" "" $NMEADatum "" "" 0]
    set tps ""
    set ix [IndexNamed TR $id]
    if {$ix == -1} then {
	foreach d $bulkdata {
	    set t [FormatLatLong [lindex $d 3] [lindex $d 4] DMS]
	    lappend t [lindex $d 1] [lindex $d 2]
	    lappend t [lindex $d 5] [lindex $d 6]
	    lappend tps $t
	}
	set trdata [list [lindex $d 0] $tps]
	set all [list Name TPoints]
	set r [FormData TR $all $trdata]
	StoreTR $ix $id $r $GetDispl
    }
    return
}

# replacing calls to ConvertDatum by call to ToDatum
proc PrepData {cmd inx} {

    # prepare WPs, RTs, or TRs data to be transferred

global GPSMANDAYS PDTYPE WPName WPSymbol WPNum WPCommt WPPosn WPDatum WPDate \
	ICIdNumber ICSymbol ICPfrmt ICPosn ICDatum RTList RTIdNumber \
	RTCommt TRName TRDatum TRTPoints NDeltas TDeltas TRNumDeltas \
	PntCount Choice SYMBOLCODE YEAR0 MONTHNAMES DateFormat CurrentTrack

    switch $cmd {
	PutWP {
	    set p $WPPosn($inx)
	    if { [string compare $WPDatum($inx) "WGS 84"] } {
		set p [ToDatum [lindex $p 0] [lindex $p 1] \
			            $WPDatum($inx) "WGS 84"]
	    }
	    set latd [lindex $p 0]
	    set longd [lindex $p 1] 
	    set sym $SYMBOLCODE($WPSymbol($inx)) 
	    if { ! [string compare $WPDate($inx) ""] } {
		set year0 $YEAR0 ; set YEAR0 1992
		set t { %Y %m %d %H %M %S }
		set s [clock format [clock seconds] -format $t]
		scan $s "%d %d %d %d %d %d" y m d h mn s
		set wpdate [DateToSecs $y $m $d $h $mn $s]
		set YEAR0 $year0
	    } else { 
		set date [split $WPDate($inx) ": -/."]
		if { ! [string compare $DateFormat "DDMMMYYYY"] } {
		    scan $date {%d %[a-zA-Z] %d %d %d %d} d mnth y h mn s
		    set m [lsearch -exact $MONTHNAMES $mnth]
		    set month [expr $m + 1]
		}
		if { ! [string compare $DateFormat "YYYY-MM-DD"] } {
		    scan $date {%d-%d%-d %d %d %d} month d y h mn s
		}
		if { ! [string compare $DateFormat "MMDDYYYY"] } {
		    scan $date {%d %d %d %d %d %d} month d y h mn s
		}
		if { ! [string compare $DateFormat "YYYYMMDD"] } {
		    scan $date {%d %d %d %d %d %d} y month d h mn s
		}
		set gpsdate [DateToSecs $y $month $d $h $mn $s]
		set wpdate [expr $gpsdate - ($GPSMANDAYS*24*3600)]
	    }
	    set wpnum [expr $WPNum($inx) - 1]
	    return [list $PDTYPE($cmd) [PrepWPData $WPNum($inx) $WPName($inx) \
		$sym $WPCommt($inx) $latd $longd $wpdate ]]
	}
	PutRT {
	    set pointlist ""
	    for {set i 0} {$i < $PntCount} {incr i} {
		lappend pointlist [lindex $RTList $i]
	    }
	    return [list $PDTYPE(PutRT) [PrepRTData $RTIdNumber($inx) \
		$PntCount $RTCommt($inx) $pointlist]]

	}
	PutIC { 
	    set p $WPPosn($inx)
	    if { [string compare $WPDatum($inx) "WGS 84"] } {
		set p [ToDatum [lindex $p 0] [lindex $p 1] \
			   $WPDatum($inx) "WGS 84"]
	    }
	    set num [string range $WPName($inx) 4 end]
	    set sym $SYMBOLCODE($WPSymbol($inx)) 
	    set latd [lindex $p 0]
	    set longd [lindex $p 1] 
	    return [list $PDTYPE($cmd) [PrepICData $num \
		$sym $latd $longd]]
	}
	PutTR {
	    set TDeltas ""
	    set points ""
	    foreach p $TRTPoints($inx) {
		lappend points [list [lindex $p 0] [lindex $p 1]]
	    }
	    set pnts ""
	    if { [string compare $TRDatum($inx) "WGS 84"] } {
		foreach p $points {
		lappend pnts [ToDatum [lindex $p 0] \
			[lindex $p 1] $TRDatum($inx) "WGS 84"]
		}
		set points $pnts
	    }
	    foreach p $points {
		set ymerc [Lat2Merc [lindex $p 0]]
		set xmerc [Long2Merc [lindex $p 1]]
		lappend tpoints [list $ymerc $xmerc] 
	    }
	    set PntCount [expr [llength $tpoints] -1]
	    set p [lindex $tpoints 0]
	    set y [lindex $p 0]	; set x [lindex $p 1]
	    set yo $y ; set xo $x
	    for {set i 1} {$i <= $PntCount} {incr i} {
		set p [lindex $tpoints $i]
		set y1 [lindex $p 0] ; set x1 [lindex $p 1]
		set dy [expr $y1 - $y] ; set dx [expr $x1 - $x]
		lappend TDeltas [list $dy $dx]
		set y $y1 ; set x $x1
	    }
	    return [list $PDTYPE($cmd) [list $inx $yo $xo $PntCount]]
	}
	PutTRd {
	    set deltas "" ; set type $PDTYPE($cmd)
	    for {set i 0} {$i < $NDeltas} {incr i} {
		set d [lindex $TDeltas 0]
		lappend deltas [lindex $d 0] [lindex $d 1]
		lappend type "word" "word"
		set TDeltas [lreplace $TDeltas 0 0] 
	    }
	    return [list $type [concat [list $CurrentTrack $NDeltas] $deltas ]]
	}

    }
}

# needing an args for compatibility with LoadGRElsIn

proc GetGPSIn {wh ixs args} {
    # get data of type $wh (in {WP, RT}), but only to replace items with
    #  given indices $ixs (that may contain -1, meaning that new items
    #  can be created)
    #  $args not used but needed for compatibility with proc LoadGRElsIn
    global GetSet

    set GetSet($wh) $ixs
    DoGetGPS $wh
    return
}


