#
#  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: gdata.tcl
#  Last change:  14 November 2001
#


## manipulation of geo-referenced data structures
#   consisting of a list of pairs $coord1 $col, in increasing order of $coord1
#   where $col is a list of pairs $coord2 $data, in increasing order of $coord2
#   and $data a list of information for each individual at $coord1 $coord2


proc AddSeqToGData {gdata coord1 coord2 info} {
    # add information to geo-referenced data structure
    # sequential search

    set ne [list $coord1 [list [list $coord2 [list $info]]]]
    set i 0
    foreach e $gdata {
	if { [set le [lindex $e 0]] > $coord1 } {
	    break
	} elseif { $le == $coord1 } {
	    set nee [list $coord2 [list $info]] ; set col [lindex $e 1]
	    set j 0
	    foreach ee $col {
		if { [set lee [lindex $ee 0]] > $coord2 } {
		    break
		} elseif { $lee == $coord2 } {
		    set nee [list $coord2 [linsert [lindex $ee 1] 0 $info]]
		    set col [lreplace $col $j $j $nee]
		    return [lreplace $gdata $i $i [list $coord1 $col]]
		}
		incr j
	    }
	    set col [linsert $col $j $nee]
	    return [lreplace $gdata $i $i [list $coord1 $col]]
	}
	incr i
    }
    return [linsert $gdata $i $ne]
}

proc AddToGData {gdata coord1 coord2 info} {
    # add information to geo-referenced data structure
    # binary search on top level list

    set ne [list $coord1 [list [list $coord2 [list $info]]]]
    if { [set b [llength $gdata]] == 0 } {
	return [list $ne]
    }
    set a 0
    while 1 {
	set i [expr ($a+$b)/2]
	set e [lindex $gdata $i]
	if { [set le [lindex $e 0]] > $coord1 } {
	    if { $a == $i } {
		break
	    }
	    set b $i
	} elseif { $le == $coord1 } {
	    set nee [list $coord2 [list $info]] ; set col [lindex $e 1]
	    set j 0
	    foreach ee $col {
		if { [set lee [lindex $ee 0]] > $coord2 } {
		    break
		} elseif { $lee == $coord2 } {
		    set nee [list $coord2 [linsert [lindex $ee 1] 0 $info]]
		    set col [lreplace $col $j $j $nee]
		    return [lreplace $gdata $i $i [list $coord1 $col]]
		}
		incr j
	    }
	    set col [linsert $col $j $nee]
	    return [lreplace $gdata $i $i [list $coord1 $col]]
	} elseif { $a == $i } {
	    incr a
	    break
	} else { set a $i }
    }
    return [linsert $gdata $a $ne]
}

proc LookupGData {gdata coord1 coord2} {
    # find information in geo-referenced data structure
    # return "" if nothing found
    # binary search on top level list

    if { [set b [llength $gdata]] == 0 } {
	return ""
    }
    set a 0
    while 1 {
	set i [expr ($a+$b)/2]
	set e [lindex $gdata $i]
	if { [set le [lindex $e 0]] > $coord1 } {
	    if { $a == $i } { break }
	    set b $i
	} elseif { $le == $coord1 } {
	    foreach ee [lindex $e 1] {
		if { [set lee [lindex $ee 0]] > $coord2 } {
		    return ""
		} elseif { $lee == $coord2 } {
		    return [lindex $ee 1]
		}
	    }
	    return ""
	} elseif { $a == $i } { break } else { set a $i }
    }
    return ""
}

proc LookupQuadrGData {gdata coord1 coord2 dc1 dc2} {
    # find information in geo-referenced data structure
    #  for each point with coordinates ($coord10,$coord20) if ($coord1,$coord2)
    #  belongs to the quadrangle centred on it and having as corners
    #  ($coord10-$dc1/2,$coord20-$dc2/2) ($coord10+$dc1/2,$coord20+$dc2/2)
    #  with $dc1 and $dc2 > 0
    # return list of information for each point
    # binary search on top level list

    if { $dc1 <= 0 || $dc2 <= 0 || [set b [llength $gdata]] == 0 } {
	return ""
    }
    set hdc1 [expr 0.5*$dc1] ; set hdc2 [expr 0.5*$dc2]
    set a 0 ; set res ""
    while 1 {
	set i [expr ($a+$b)/2]
	set e [lindex $gdata $i]
	if { [set le [lindex $e 0]]-$hdc1 > $coord1 } {
	    if { $a == $i } { return $res }
	    set b $i
	} elseif { $le+$hdc1 >= $coord1 } {
	    foreach ee [lindex $e 1] {
		if { [set lee [lindex $ee 0]]-$hdc2 > $coord2 } {
		    break
		} elseif { $lee+$hdc2 >= $coord2 } {
		    set res [concat $res [lindex $ee 1]]
		}
	    }
	    break
	} elseif { $a == $i } { break } else { set a $i }
    }
    for { set j [expr $i+1] } { $j != $b } { incr j } {
	set e [lindex $gdata $j]
	if { [set le [lindex $e 0]]-$hdc1 > $coord1 } { break }
	if {  $le+$hdc1 >= $coord1 } {
	    foreach ee [lindex $e 1] {
		if { [set lee [lindex $ee 0]]-$hdc2 > $coord2 } {
		    break
		} elseif { $lee+$hdc2 >= $coord2 } {
		    set res [concat $res [lindex $ee 1]]
		}
	    }
	}
    }
    incr a -1
    for { set j [expr $i-1] } { $j != $a } { incr j -1 } {
	set e [lindex $gdata $j]
	if { [set le [lindex $e 0]]+$hdc1 < $coord1 } { break }
	if {  $le-$hdc1 <= $coord1 } {
	    foreach ee [lindex $e 1] {
		if { [set lee [lindex $ee 0]]-$hdc2 > $coord2 } {
		    break
		} elseif { $lee+$hdc2 >= $coord2 } {
		    set res [concat $res [lindex $ee 1]]
		}
	    }
	}
    }
    return $res
}

