#
#  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: lists.tcl
#  Last change:  23 October 2008
#

proc ChangeOnStateList {wh st} {
    # change state of items list when list becomes empty/non-empty
    #  $wh in $TYPES or LAP
    #  $st in {normal, disabled}
    global LsW RcW RcMenu GPSState WConf RECTYPES

    # entries in the type list menu and in the type sub-menu of Data menu
    switch $wh {
	GR {
	    set es "1 2 4 5 6 7 8" ; set aes "1 2 3 4 5 6 7"
	}
	LAP {
	    set es "0 2" ; set aes "0 2"
	}
	default {
	    set es "1 2 5 6" ; set aes "1 2 4 5"
	}
    }
    if { [set ix [lsearch -exact $RECTYPES $wh]] != -1 } {
	# reconfigure receiver window/menu
	if { $wh == "GR" } {
	    set bs "frget.frget2.getGR frput.putGR"
	    $RcMenu.gm entryconfigure $WConf(rec,getmn,GR) -state $st
	    $RcMenu.ptm entryconfigure $WConf(rec,putmn,GR) -state $st
	} elseif { $wh != "IC" && $wh != "LAP" } {
	    set bs "frput.put$wh"
	    $RcMenu.ptm entryconfigure $WConf(rec,putmn,$wh) -state $st
	} else { set bs "" }
	if { $st == "normal" && $GPSState == "online" } {
	    set sg normal
	} else { set sg disabled }
	foreach b $bs {
	    $RcW.$b configure -state $sg
	}
    }
    foreach e $es {
	$LsW.frl$wh.frb.file.m entryconfigure $e -state $st
    }
    if { [set p $WConf(additemstate)] != "" } {
	foreach e $aes {
	    ${p}$wh entryconfigure $e -state $st
	}
    }
    return
}

proc ClearList {wh} {
    # forget all items in a list; $wh in $TYPES
    global ListInds List EdWindow GMEd MESS TXT

    if { $ListInds($wh) == "" || \
	    ! [GMConfirm [format $MESS(forgetall) $TXT(name$wh)]] } { return }
    set ixs $ListInds($wh)
    if { [winfo exists $EdWindow($wh)] } {
	GMMessage [format $MESS(cantfgted) $TXT(name$wh)]
	set ixs [Delete $ixs $GMEd($wh,Index)]
    }
    # ForgetSeveral calls procs that update $ListInds($wh)
    ForgetSeveral $wh $ixs
    return
}

proc Count {wh} {
    # count items in list; $wh in $TYPES
    global Number MESS TXT

    GMMessage [format $MESS(counted) $Number($wh) $TXT(name$wh)]
    return
}

proc OpenListItem {wh} {
    # edit or display selected item in a list; $wh in $TYPES
    global LsW ListInds

    set s [$LsW.frl$wh.frl.box curselection]
    if { $s != "" } {
	OpenItem $wh [lindex $ListInds($wh) $s]
    }
    return
}

proc ToggleDisplayItem {wh sel} {
    # un-/display on map selected item in a list
    #  $wh in $TYPES or LAP; $sel is selection index in list
    global ListInds EdWindow GMEd ${wh}Displ Number

    if { $wh == "LAP" } { return }
    if { $Number($wh)>0 && $sel != "" } {
	set index [lindex $ListInds($wh) $sel]
	set w $EdWindow($wh)
	if { [winfo exists $w] && $GMEd($wh,Index) == $index } {
	    Raise $w ; bell
	    return
	}
	if { [set [set wh]Displ($index)] } {
	    UnMap $wh $index
	} else { PutMap $wh $index }
    }
    return
}

proc ToggleDisplayNamed {wh name} {
    # un-/display on map item with given name
    #  $wh in $TYPES or LAP
    global EdWindow GMEd ${wh}Displ

    if { $wh == "LAP" } { return }
    if { [set index [IndexNamed $wh $name]] == -1 } { return }
    set w $EdWindow($wh)
    if { [winfo exists $w] && $GMEd($wh,Index) == $index } {
	Raise $w ; bell
    } elseif { [set [set wh]Displ($index)] } {
	UnMap $wh $index
    } else { PutMap $wh $index }
    return
}

proc ListAdd {wh index} {
    # add new item with given index to non-empty list; $wh in $TYPES or LAP
    global LsW ListInds Storage CMDLINE LAPStart

    if { $CMDLINE } { return }

    set ids [lindex $Storage($wh) 0]
    global $ids
    set name [set [set ids]($index)]
    if { $ListInds($wh) == "" } {
	set ListInds($wh) $index
	$LsW.frl$wh.frl.box insert end $name
	ChangeOnStateList $wh normal
    } elseif { $wh != "LAP" } {
	set a 0 ; set b [expr [$LsW.frl$wh.frl.box size]-1]
	while 1 {
	    set i [expr int(floor(($a+$b)/2))]
	    if { [string compare $name [$LsW.frl$wh.frl.box get $i]] < 0 } {
		set b $i
	    } else { set a $i }
	    if { $b == $a } {
                if { [string compare $name \
			[$LsW.frl$wh.frl.box get $a]] > 0 } {
		    incr a
		}
		$LsW.frl$wh.frl.box insert $a $name
		set ListInds($wh) [linsert $ListInds($wh) $a $index]
		break
            }
	    if { $b == $a+1 } {
		if { [string compare $name \
			[$LsW.frl$wh.frl.box get $a]] > 0 } {
		    incr a
		    if { [string compare $name \
			    [$LsW.frl$wh.frl.box get $a]] > 0 } {
			incr a
		    }
		}
		$LsW.frl$wh.frl.box insert $a $name
		set ListInds($wh) [linsert $ListInds($wh) $a $index]
		break
	    }
	}
    } else {
	# LAP: order by start date
	set secs [lindex $LAPStart($index) 1]
	set a 0 ; set nf 1
	foreach ix $ListInds(LAP) {
	    if { [lindex $LAPStart($ix) 1] <= $secs } {
		set nf 0 ; break
	    }
	    incr a
	}
	if { $nf } { set a end }
	$LsW.frl$wh.frl.box insert $a $name
	set ListInds($wh) [linsert $ListInds($wh) $a $index]
    }
    return
}

proc ListDelete {wh index} {
    # delete item with given index from list; $wh in $TYPES or LAP
    global LsW ListInds

    set n [lsearch -exact $ListInds($wh) $index ]
    $LsW.frl$wh.frl.box delete $n
    set ListInds($wh) [lreplace $ListInds($wh) $n $n]
    if { [$LsW.frl$wh.frl.box size] == 0 } {
	ChangeOnStateList $wh disabled
    }
    return
}

proc ListDeleteSeveral {wh ixs} {
    # delete items with given indices from list; $wh in $TYPES or LAP
    #  $ixs has the same order of $ListInds($wh) although with some
    #    elements missing
    global LsW ListInds

    if { $ixs == "" } { return }
    set exs $ListInds($wh) ; set ks ""
    set bx $LsW.frl$wh.frl.box ; set ib 0
    while { $ixs != "" } {
	set c 0
	foreach ex $exs ix $ixs {
	    if { $ex == $ix } {
		$bx delete $ib ; incr c
	    } else {
		lappend ks $ex ; incr ib
		break
	    }
	}
	set exs [lrange $exs [expr $c+1] end]
	set ixs [lrange $ixs $c end]
    }
    set ListInds($wh) [concat $ks $exs]
    if { $ib == 0 } {
	ChangeOnStateList $wh disabled
    }
    return
}

proc ListDeleteAll {wh} {
    # delete all items from list; $wh in $TYPES or LAP
    global LsW ListInds

    $LsW.frl$wh.frl.box delete 0 end
    set ListInds($wh) ""
    ChangeOnStateList $wh disabled
    return
}

proc ChooseItems {wh args} {
    # create modal dialog for selecting from list of items
    #  $wh in $TYPES or LAP
    #  $args is empty or a list whose head is in {single, many, many_0}
    #    for a single element, at least one element, and zero or more
    #    elements, and whose tail is a pair that is passed on to
    #    GMChooseFrom and describes parameters
    # return indices of items selected or "" if cancelled
    global LISTWIDTH Storage ListInds TXT

    set ids [lindex $Storage($wh) 0]
    global $ids
    set ns ""
    foreach i $ListInds($wh) { lappend ns [set [set ids]($i)] }
    if { $args != "" } {
	return [GMChooseFrom [lindex $args 0] \
		             [list $TXT(select) $TXT(name$wh)] \
		             $LISTWIDTH $ns $ListInds($wh) \
			     [lindex $args 1] [lindex $args 2]]
    }
    return [GMChooseFrom many [list $TXT(select) $TXT(name$wh)] \
		         $LISTWIDTH $ns $ListInds($wh)]
}

proc ChItemsCall {wh mode comm args} {
    # select from list of items and call command
    #  $wh in $TYPES or LAP
    #  $mode in {single, many, many_0} as in proc ChooseItems
    #  $comm is command name to call as
    #     $comm ARG0 ... ARGn NAMES

    if { [set ixs [ChooseItems $wh $mode]] == "" } { return }
    set names [Apply $ixs NameOf $wh]
    eval $comm $args $names
    return
}

proc InputToGR {whs notwhs preproc proc postproc fmt} {
    # input data according to contents of group(s)
    # items of types in $whs (except those in $notwhs) will be read in
    #  $preproc will be called immediately before input is really required
    #     and should return 0 unless operation is to be cancelled;
    #     arguments: GR and $fmt; proc Ignore can be used if nothing is to
    #     be done
    #  $proc is the input procedure, whose arguments will be:
    #     type of item to input, list of indices and $fmt if not "receiver"
    #  $postproc will be called after the last call to $proc; single
    #     argument: GR; proc Ignore can be used if nothing is to be done
    #  $fmt is a either a file format (as used in the FILEFORMAT array)
    #     or "receiver"
    global MESS TXT GCHow ListInds Storage FILEFORMAT

    set gts "" ; set ts ""
    foreach t $whs {
	if { [lsearch -exact $notwhs $t] == -1 } {
	    lappend gts $t ; lappend ts $TXT(name$t)
	}
    }
    set vals [list $TXT(notinGR) $TXT(onlyinGR)]
    if { $fmt != "receiver" && $FILEFORMAT($fmt,filetype) == "data" } {
	set mode many
    } else { set mode single }
    while 1 {
	set whs [GMChooseFrom many $MESS(readwhat) 6 $ts $gts \
		     GCHow [list $vals]]
	set compl [string compare $GCHow $TXT(onlyinGR)]
	if { [set i [lsearch -exact $whs GR]] != -1 } {
	    set whs [lreplace $whs $i $i]
	    set rec 1
	} else { set rec 0 }
	if { $mode == "single" && [llength $whs] > 1 } {
	    GMMessage $MESS(importonly1)
	} else { break }
    }
    if { $whs == "" || [set ixs [ChooseItems GR]] == "" } { return }
    if { [$preproc GR $fmt] } { return }
    if { $mode != "single" } {
	set ll ""
	foreach wh $whs {
	    if { [set l [GRsElements $ixs $rec $wh]] != "" } {
		if { $compl } {
		    set ids [lindex $Storage($wh) 0]
		    global $ids
		    set l [linsert [Complement [array names $ids] $l] 0 -1]
		}
		lappend ll $l
	    }
	}
	if { $ll != "" } { $proc $whs $ll $fmt }
    } else {
	# single
	if { [set l [GRsElements $ixs $rec $whs]] != "" } {
	    if { $compl } {
		set ids [lindex $Storage($whs) 0]
		global $ids
		set l [linsert [Complement [array names $ids] $l] 0 -1]
	    }
	    if { $fmt != "receiver" } {
		$proc $whs [list $l] $fmt
	    } else {
		$proc $whs $l
	    }
	}
    }
    $postproc GR
    return
}
