#!/bin/sh
# restart using wish \
  exec wish8.3 "$0" ${1+"$@"}

# $Id: stockwatcher,v 1.13 2000/06/06 02:29:01 soul Exp $
# stockwatcher, a stock watcher program
#  Copyright 2000 Laurent Duperval <lduperval@sprint.ca>
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 
#   * Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#
#   * Redistributions in binary form must reproduce the above
#     copyright notice, this list of conditions and the following
#     disclaimer in the documentation and/or other materials provided
#     with the distribution.
#
#   * The name of the Copyright holder may not be used to endorse or promote
#     products derived from this software without specific prior written
#     permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Portions of this software are copyrighted by Tom Poindexter, 1999.

if {! [info exists env(TCLTICKER_LIB)]} {
    lappend auto_path [file join [file dirname $argv0] lib]
} else {
    lappend auto_path $env(TCLTICKER_LIB)
}   

package require http 2.0
package require BWidget 1.3
package require BLT 2.4

wm title . "Stock Watcher"

# global variables used
global refresh tick_speed tick_font tick_size tick_ontop tick_round
global ticker_symbols data 
global service_list service
global proxy_host proxy_port
global mail_host  mail_port

global index last_time_stamp

# 'index' is used to cycle through ticker symbols in ticker_symbols array
set index 0

# last_time_stamp is timestamp of last data refresh
set last_time_stamp ""

# 'next_refresh' - after id of next refresh request
global next_refresh
set next_refresh ""

# The folio variable contains the data for all the portfolio information.
# These are the indices used:
#
# $symbol,transactions: Total number of transactions for a given symbol. Each
# transaction corresponds to a purchase of shares. So if, for example, you buy
# 30 stocks of SUNW at 9:30 am, 100 at 10:15 am and 6 at 1:00 pm, that's 3
# transactions for SUNW.
# $symbol,$n,qty: Number of shares for transaction $n on symbol $symbol
# $symbol,$n,value: Current value of transaction $n on symbol $symbol
# $symbol,$n,delta: Delta of transaction $n on symbol $symbol, compared to the
# original purchase price
# $symbol,$n,daily: Delta of transaction $n on symbol $symbol compared to
# previous trade day
# $symbol,$n,date: Purchase date (and time)
# $symbol,$n,price: Purchase price of transaction $n for symbol $symbol
# $symbol,$n,purchase: Total value of purchase of transaction $n on symbol
# $symbol
# $symbol,$n,widget: Widget base name to display the data of transaction $n
# for symbol $symbol
#
global folio

proc doticker {t} {
    global tick_speed tick_ontop ticker_symbols data tcl_platform
    global index last_time_stamp
	global folio
    if {! [info exists ticker_symbols(1)] } {
	$t configure -state normal
	$t delete 1.0 end
	$t configure -state disabled
	update idletasks
	get_parms "No ticker symbols have been entered,\n\
		   please enter symbols to display."
        after 100 [list doticker $t]
	return
    }
    # find out how much text on the line, change laurent's code to use pixels
    set wid [winfo width $t]
    incr wid 20 	;# make sure new messages get appended before needed
    set len [font measure [$t cget -font] [$t get 1.0 1.end]]
    if {[expr {$len < $wid}]} {
		set msg "  . . .  "
        if {![info exists ticker_symbols($index)]} {
            set index 1
	    set msg "  (click here!)  $last_time_stamp  . . .  "
        } else {
	    set msg "  . . .  "
        }
        set symbol $ticker_symbols($index)
		set sign $data($symbol,sign)
		set message "$symbol $data($symbol,last) $data($symbol,delta)"
		if {[info exists folio($symbol,transactions)] && [string length $data($symbol,last)] } {
			for {set i 0} {$i < $folio($symbol,transactions)} {incr i} {
				catch {set folio($symbol,$i,value)  \
					[format "%.2f" [expr {$data($symbol,last)*$folio($symbol,$i,qty)}]]}
				catch {set folio($symbol,$i,daily)  \
					[format "%.2f" [expr {$data($symbol,delta)*$folio($symbol,$i,qty)}]]}
				catch {set folio($symbol,$i,delta)  \
					[format "%.2f" [expr {$folio($symbol,$i,value)-$folio($symbol,$i,purchase)}]]}
				::tickergraph::addData $symbol [clock seconds] $data($symbol,last)
			}
		}
        $t configure -state normal
        $t insert end $msg fill
        $t insert end $message tag$sign
        $t configure -state disabled
        incr index
	# windows doesn't support visibility events, so try to force it up if 
	# tick_ontop is wanted, may cause some flicker, or work poorly :-(
	if {$tick_ontop && "$tcl_platform(platform)" == "windows"} {
	    wm withdraw .  ; update
	    raise .        ; update
	    wm deiconify . ; update
	    raise .        ; update
	}
    }
    $t configure -state normal
    $t delete 1.0
    $t configure -state disabled
    after $tick_speed [list doticker $t]
}

proc font_trace {args} {
    global tick_font tick_size
    .t configure -font "$tick_font $tick_size"
}

proc start_ticker {} {
    global tick_speed tick_font tick_size tick_ontop
    text .t  -relief sunken -bd 0 -wrap none -bg black -state disabled  \
	-exportselection 0 -height 1 -width 60 \
	-font "$tick_font $tick_size" -highlightthickness 0
    bind .t <Button-1> get_parms
    bind .t <Button-2> get_parms
    bind .t <Button-3> get_parms
    bind . <Control-q> Exit
	bind . <Control-e> get_parms
    .t tag configure fill -foreground white
    .t tag configure tag  -foreground white
    .t tag configure tag= -foreground yellow
    .t tag configure tag- -foreground red
    .t tag configure tag+ -foreground green
    pack .t  -fill x -pady 6 -padx 4
    .t configure -bg black

    trace variable tick_font w font_trace
    trace variable tick_size w font_trace
    # allow window to be resized in width, but not in height
    #wm resizable . 1 0
    # for unix, make window always on top if desired
    bind . <Visibility> {
	if {$tick_ontop && \
	    ( "%s" == "VisibilityPartiallyObscured" || \
	      "%s" == "VisibilityFullyObscured") } {
	    raise .
	}
    }

    # Create the scrolled window that will display folio information

    create_folio

    doticker .t
}

proc addNotebookPage {nb symbol} {
    global folio
    global ticker_symbols
	# Remove . from symbol names if any
	regsub -all {\.} $symbol "_" wsymbol
	set wsymbol [string tolower $wsymbol]
	set nbframe [$nb insert end $wsymbol -text "$symbol" ]
    set sw [ScrolledWindow $nbframe.sw -borderwidth 5]
	set swfolio [ScrollableFrame $sw.folio -constrainedwidth 1]

    $sw setwidget $swfolio

    set subf [$swfolio getframe]
	set folio($symbol,nbPage) $subf
	bind . <Control-n> [list newTransaction]
	#pack $add -anchor w

    # Each line of $swfolio consists of the following:
    # Label:  Qty  Cost TotalCost CurrentValue Delta

    label $subf.labelLabel -text Symbol -anchor w
    label $subf.qtyLabel -text Quantity
    label $subf.costLabel -text "Purchase\nCost"
    label $subf.valueLabel -text "Current\nValue"
    label $subf.deltaLabel -text "Delta"
    label $subf.dailyDeltaLabel -text "Daily\nDelta"

    grid $subf.labelLabel $subf.qtyLabel $subf.costLabel $subf.valueLabel \
        $subf.deltaLabel $subf.dailyDeltaLabel -sticky nsew

	for {set i 0} {$i < [lindex [grid size $subf] 0]} {incr i} {
		grid columnconfigure $subf $i -weight 1
	}

	pack $sw -side bottom -fill both -expand true

	set subf
}

proc create_folio {} {
    global folio
    global ticker_symbols

    set add [button .add -text "New Transaction" -command [list newTransaction]]
	pack $add -side top
	set nb [NoteBook .nb]

	set symbolList {}
	# Put the symbols in alphabetical order
	foreach ix [array names ticker_symbols] {
	    lappend symbolList $ticker_symbols($ix)
	}

	foreach symbol [lsort $symbolList]  {
		if {[info exists folio($symbol,transactions)]} {
			set subf [addNotebookPage $nb $symbol]

			for {set i 0} {$i < $folio($symbol,transactions)} {incr i} {
				insertNewTransaction $symbol $i
			}

			for {set i 0} {$i < [lindex [grid size $subf] 0]} {incr i} {
				grid columnconfigure $subf $i -weight 1
			}
		}

	}

	create_summary $nb

	pack $nb -expand true -fill both
}

proc create_summary {nb} {
	global ticker_symbols
    global folio

	set nbframe [$nb insert 0 Summary -text "Summary" ]
    set sw [ScrolledWindow $nbframe.sw -borderwidth 5]
    set swfolio [ScrollableFrame $sw.folio -constrainedwidth 1]

    $sw setwidget $swfolio

    set subf [$swfolio getframe]
    #set add [button $nbframe.add -text "New Transaction" -command [list newTransaction $subf]]
	bind . <Control-n> [list newTransaction]
	#pack $add -anchor w
	set folio(summary,widget) $subf

    # Each line of $swfolio consists of the following:
    # Label:  Qty  Cost TotalCost CurrentValue Delta

    label $subf.labelLabel -text Symbol -anchor w
    label $subf.qtyLabel -text Quantity
    label $subf.costLabel -text "Purchase\nCost"
    label $subf.valueLabel -text "Current\nValue"
    label $subf.deltaLabel -text "Delta"
    label $subf.dailyDeltaLabel -text "Daily\nDelta"

    grid $subf.labelLabel $subf.qtyLabel $subf.costLabel $subf.valueLabel \
        $subf.deltaLabel $subf.dailyDeltaLabel -sticky nsew
	set symbolList {}
	# Put the symbols in alphabetical order
	foreach ix [array names ticker_symbols] {
	    lappend symbolList $ticker_symbols($ix)
	}
	foreach symbol [lsort $symbolList]  {
		# Remove . from symbol names if any
		regsub -all {\.} $symbol "_" wsymbol
		set wsymbol [string tolower $wsymbol]
		if {[info exists folio($symbol,transactions)]} {
			set folio($symbol,total) 0
			insertNewSummary $symbol
		}
	}
	
	pack $sw -side bottom -fill both -expand true
    for {set i 0} {$i < [lindex [grid size $subf] 0]} {incr i} {
		grid columnconfigure $subf $i -weight 1
    }

	$nb raise Summary
}

proc validateSymbol {win} {
	global data
	set symbol [$win get]
	return [info exists data($symbol,last)]
}

proc newTransaction {} {
	global ticker_symbols

    set dlg .addDlg
    set now [clock seconds]
    set date [clock format $now -format "%m/%d/%Y"]
    set time [clock format $now -format "%H:%M"]
    
    if {[winfo exists $dlg]} {
	set dlgf [$dlg getframe]
    } else {
	Dialog $dlg -cancel 1 -default 0 -modal local -parent . 
	set dlgf [$dlg getframe]
	set widgets [list]
	lappend widgets [LabelFrame $dlgf.symbolFrame -text "Symbol:"]
	pack [ComboBox $dlgf.symbolEntry -editable 0 ] -in [$dlgf.symbolFrame getframe]
	lappend widgets [LabelEntry $dlgf.qtyEntry -label "Quantity:" \
	    -validate key -validatecommand [list string is double %P]]
	lappend widgets [LabelEntry $dlgf.priceEntry -label "Purchase Price:" \
	    -validate key -validatecommand [list string is double %P]]
	lappend widgets [LabelEntry $dlgf.dateEntry -label Date:]
	lappend widgets [LabelEntry $dlgf.timeEntry -label Time:]
	eval LabelFrame::align $widgets
	eval pack $widgets
	$dlg add -text "Ok" -command [list createTransactionData $dlg $dlgf]
	$dlg add -text "Cancel"
    }

    $dlgf.dateEntry delete 0 end
    $dlgf.dateEntry insert 0 $date
    $dlgf.timeEntry delete 0 end
    $dlgf.timeEntry insert 0 $time
	foreach ix [array names ticker_symbols] {
	    lappend symbolList $ticker_symbols($ix)
	}
	$dlgf.symbolEntry configure -values [lsort $symbolList]
    $dlg draw
	
}

proc createTransactionData {dlg win} {
    global folio
    global data

    set symbol [lindex [$win.symbolEntry cget -values] [$win.symbolEntry getvalue]]
    set qty [$win.qtyEntry get]
    set price [$win.priceEntry get]
    set date [$win.dateEntry get]
    set time [$win.timeEntry get]

	if {![string length $symbol]} {
		return
	}

	if {![string length $qty]} {
		set qty 0
	}

	if {![string length $price]} {
		set price 0.0
	}

    if {[info exists folio($symbol,transactions)]} {
		set curTsx $folio($symbol,transactions)
		incr folio($symbol,transactions)
		if {[info exists folio($symbol,realtransactions)]} {
			incr folio($symbol,realtransactions)
		}
    } else {
		set curTsx 0
		set folio($symbol,transactions) 1
    }

	set folio($symbol,highestTsx) $curTsx
    set folio($symbol,$curTsx,qty) $qty
    set folio($symbol,$curTsx,price) $price
    set folio($symbol,$curTsx,purchase) [format "%.2f" [expr {$qty*$price}]]
    set folio($symbol,$curTsx,date) [clock scan "$date $time"]
    if {[info exists data($symbol,last)] && \
		[string length $data($symbol,last)]} {
		set folio($symbol,$curTsx,value) \
			[format "%.2f" [expr {$data($symbol,last)*$folio($symbol,$curTsx,qty)}]]
	} else {
		set folio($symbol,$curTsx,value) 0.00
	}

    if {[info exists data($symbol,delta)] && \
		[string length $data($symbol,delta)]} {
		set folio($symbol,$curTsx,daily) \
			[format "%.2f" [expr {$data($symbol,delta)*$folio($symbol,$curTsx,qty)}]]
	} else {
		set folio($symbol,$curTsx,daily) 0.00
	}
	set folio($symbol,$curTsx,delta) \
		[format "%.2f" [expr {$folio($symbol,$curTsx,value)-$folio($symbol,$curTsx,purchase)}]]

	write_folio

    insertNewTransaction $symbol $curTsx

    $dlg withdraw
}

proc deleteTransaction {symbol tsx} {
    global folio
    
    set answer [tk_messageBox -type yesno \
	-message "Delete transaction $symbol-$tsx?"]

    if {[string equal "yes" $answer]} {
	set wbase $folio($symbol,$tsx,widget)
	destroy ${wbase}labelLabel ${wbase}qtyLabel ${wbase}costLabel \
	${wbase}valueLabel ${wbase}deltaLabel ${wbase}dailyDeltaLabel \
	${wbase}editButton ${wbase}delButton
	foreach ix [array names folio "$symbol,$tsx,*"] {
	    unset folio($ix)
	}
	if {[info exists folio($symbol,realtransactions)]} {
	    incr folio($symbol,realtransactions) -1
	} else {
	    set folio($symbol,realtransactions) $folio($symbol,transactions)
	    incr folio($symbol,realtransactions) -1
	}
    }
}

proc editTransaction {symbol tsx} {
    global folio

    set dlg .editDlg
    
    if {[winfo exists $dlg]} {
	set dlgf [$dlg getframe]
    } else {
	Dialog $dlg -cancel 1 -default 0 -modal local -parent . 
	set dlgf [$dlg getframe]
	set widgets [list]
	lappend widgets [LabelEntry $dlgf.symbolEntry -label "Symbol:" \
	    -state disabled]
	lappend widgets [LabelEntry $dlgf.qtyEntry -label "Quantity:" \
	    -validate key -validatecommand [list string is double %P]]
	lappend widgets [LabelEntry $dlgf.priceEntry -label "Purchase Price:" \
	    -validate key -validatecommand [list string is double %P]]
	lappend widgets [LabelEntry $dlgf.dateEntry -label Date: ]
	lappend widgets [LabelEntry $dlgf.timeEntry -label Time: ]
	eval LabelFrame::align $widgets
	eval pack $widgets
	$dlg add -text "Ok" 
	$dlg add -text "Cancel" -command [list $dlg withdraw]
    }

	$dlg itemconfigure 0 -command [list endEdit $dlg $dlgf $symbol $tsx]
    $dlgf.symbolEntry configure -text $symbol
    $dlgf.qtyEntry configure -text $folio($symbol,$tsx,qty)
    $dlgf.priceEntry configure -text $folio($symbol,$tsx,price)
    $dlgf.dateEntry configure -text [clock format $folio($symbol,$tsx,date) -format "%m/%d/%Y"]
    $dlgf.timeEntry configure -text [clock format $folio($symbol,$tsx,date) -format "%H:%M"]
    $dlg draw

}

proc showGraph {symbol} {
	# check if graphing window exists
	::tickergraph::showGraph $symbol
}

proc endEdit {dlg win symbol tsx} {
    global folio

    set qty [$win.qtyEntry get]
    set price [$win.priceEntry get]
    set date [$win.dateEntry get]
    set time [$win.timeEntry get]

	if {![string length $qty]} {
		set qty 0
	}

	if {![string length $price]} {
		set price 0.0
	}

    set folio($symbol,$tsx,qty) $qty
    set folio($symbol,$tsx,price) $price
    set folio($symbol,$tsx,purchase) [format "%.2f" [expr {$qty*$price}]]
    set folio($symbol,$tsx,date) [clock scan "$date $time"]

    $dlg withdraw
}

proc insertNewTransaction {symbol tsx } {
    global folio

	if {![info exists folio($symbol,nbPage)]} {
		addNotebookPage .nb $symbol
	}

	# Don't insert summary information when first building the interface. At
	# that time, the summary is built after all the portfolio information has
	# been set up.
	if {[info exists folio(summary,widget)] && ![info exists folio($symbol,summary,widget)]} {
		insertNewSummary $symbol
	} else {
	}

	if {[info exists folio($symbol,highestTsx)]} {
		if {$tsx > $folio($symbol,highestTsx)} {
			set folio($symbol,highestTsx) $tsx
		}
	} else {
		set folio($symbol,highestTsx) $tsx
	}
	set subf $folio($symbol,nbPage)

    # Remove . from symbol names if any
    regsub -all {\.} $symbol "_" wsymbol
    set wsymbol [string tolower $wsymbol]
    set folio($symbol,$tsx,widget) $subf.$wsymbol$tsx
    set wbase $folio($symbol,$tsx,widget)
    set widgets ""
    lappend widgets [label ${wbase}labelLabel -anchor w -text $symbol]
    lappend widgets [label ${wbase}qtyLabel -anchor e -textvariable folio($symbol,$tsx,qty)]
    lappend widgets [label ${wbase}costLabel -anchor e -textvariable folio($symbol,$tsx,purchase)]
    lappend widgets [label ${wbase}valueLabel -anchor e -textvariable folio($symbol,$tsx,value)]
    lappend widgets [label ${wbase}deltaLabel -anchor e -textvariable folio($symbol,$tsx,delta)]
    lappend widgets [label ${wbase}dailyDeltaLabel -anchor e -textvariable folio($symbol,$tsx,daily)]
	lappend widgets [button ${wbase}editButton -text Edit \
	-command [list editTransaction $symbol $tsx]]
	lappend widgets [button ${wbase}delButton -text Delete \
	-command [list deleteTransaction $symbol $tsx]]
    eval grid $widgets -sticky news
}

proc insertNewSummary {symbol} {
    global folio
	set subf $folio(summary,widget)

    # Remove . from symbol names if any
    regsub -all {\.} $symbol "_" wsymbol
    set wsymbol [string tolower $wsymbol]
    set folio($symbol,summary,widget) $subf.$wsymbol
    set wbase $folio($symbol,summary,widget)
    set widgets ""
    lappend widgets [label ${wbase}labelLabel -anchor w -text $symbol]
    lappend widgets [label ${wbase}qtyLabel -anchor e -textvariable folio($symbol,summary,qty)]
    lappend widgets [label ${wbase}costLabel -anchor e -textvariable folio($symbol,summary,purchase)]
    lappend widgets [label ${wbase}valueLabel -anchor e -textvariable folio($symbol,summary,value)]
    lappend widgets [label ${wbase}deltaLabel -anchor e -textvariable folio($symbol,summary,delta)]
    lappend widgets [label ${wbase}dailyDeltaLabel -anchor e -textvariable folio($symbol,summary,daily)]
	lappend widgets [button ${wbase}graphButton -text Graph \
		-command [list ::tickergraph::showGraph $symbol]]
	::tickergraph::createGraph $symbol
    eval grid $widgets -sticky news
}

proc do_refresh {} {
    global service
    if {[catch {$service} svc_rc]} {
        set_last_time " quote_retrieval_error "
    } elseif {$svc_rc} {
        set_last_time
    } else {
        # service module ran, but didn't complete, don't do anyting
    }
}

proc refresh_data {args} {
    global refresh service next_refresh index
    if {"[lindex $args 0]" == "restart"} {
	catch {after cancel $next_refresh}
    }

    after 0 do_refresh

    set next_refresh [after [expr {$refresh * 60000}] "refresh_data" ]
}


proc get_parms {{msg {}}} {
    global refresh tick_speed tick_font tick_size tick_ontop tick_round
    global index ticker_symbols data 
    global service_list service
    global proxy_host proxy_port
    global mail_host  mail_port
    global temp_refresh temp_tick_speed 
    global temp_tick_ontop temp_tick_round doParms
    global temp_proxy_host temp_proxy_port
    global temp_mail_host  temp_mail_port

    catch {
      bind .t <Button-1> {}
      bind .t <Button-2> {}
      bind .t <Button-3> {}
    }
    set temp_refresh $refresh
    set temp_tick_speed $tick_speed
    set temp_tick_ontop $tick_ontop
    set temp_tick_round $tick_round
    set temp_proxy_host $proxy_host
    set temp_proxy_port $proxy_port
    set temp_mail_host  $mail_host
    set temp_mail_port  $mail_port

    # turn off ticker on top during dialog
    set save_tick_ontop $tick_ontop
    set tick_ontop 0

    catch {destroy .p}
    toplevel .p -class Dialog
    wm title .p "TclTicker: Set Parameters"
    wm withdraw .p
    update

    if {[string length $msg]} {
	label .p.msg -text $msg -relief raised
	pack .p.msg -side top -fill x
    }

    label .p.l0 -text "version 1.2 - Copyright 1999 Tom Poindexter"
    pack .p.l0 -side top

    frame .p.q
    label .p.q.l1 -text "quote service:"
    pack .p.q.l1 -side left
    scrollbar .p.q.scr -orient vertical -command ".p.q.service yview"
    listbox .p.q.service -width 30 -height 3 \
		-exportselection 0 \
		-selectmode single -yscrollcommand ".p.q.scr set"
    pack .p.q.service -side left -fill y
    if {[llength $service_list] > 3} {
	pack .p.q.scr -side left -fill y
    }
    pack .p.q -side top -pady 4

    frame .p.p
    label .p.p.l1 -text "http proxy host:"
    entry .p.p.ph -width 20 -textvariable temp_proxy_host
    label .p.p.l2 -text " port:"
    entry .p.p.pp -width 4 -textvariable temp_proxy_port
    pack .p.p.pp .p.p.l2 .p.p.ph .p.p.l1 -side right -padx 3 
    pack .p.p -side top -padx 4

    frame .p.m
    label .p.m.l1 -text "smtp relay host:"
    entry .p.m.mh -width 20 -textvariable temp_mail_host
    label .p.m.l2 -text " port:"
    entry .p.m.mp -width 4 -textvariable temp_mail_port
    pack .p.m.mp .p.m.l2 .p.m.mh .p.m.l1 -side right -padx 3 
    pack .p.m -side top -pady 4 -padx 4

    frame .p.s
    scale .p.s.tick -label "ticker delay (milliseconds)" -from 100 -to 500 \
		-orient horizontal -resolution 50 \
		-length 160 -variable temp_tick_speed 
    pack .p.s.tick -side  left
    scale .p.s.refresh -label "data refresh (minutes)"   -from 1 -to 15 \
		-orient horizontal -length 160 -variable temp_refresh 
    pack .p.s.refresh -side right
    pack .p.s -side top

    frame .p.t
    label .p.t.l1 -text "ticker font:"
    scrollbar .p.t.fscr -orient horizontal -command ".p.t.fon yview" 
    listbox .p.t.fon -height 1 -width 10 -highlightthickness 0 \
		-selectborderwidth 0 \
		-exportselection 0 -selectmode single \
		-yscrollcommand ".p.t.fscr set" 
    .p.t.fon configure -selectbackground [.p.t.fon cget -background]
    .p.t.fon configure -selectforeground [.p.t.fon cget -foreground]
    label .p.t.l2 -text "    size:"
    scrollbar .p.t.sscr -orient horizontal -command ".p.t.siz yview"
    listbox .p.t.siz -height 1 -width 3 -highlightthickness 0 \
		-selectborderwidth 0 \
		-exportselection 0 -selectmode single \
		-yscrollcommand ".p.t.sscr set"
    .p.t.siz configure -selectbackground [.p.t.siz cget -background]
    .p.t.siz configure -selectforeground [.p.t.siz cget -foreground]
    pack .p.t.l1 .p.t.fon  .p.t.fscr .p.t.l2 .p.t.siz .p.t.sscr -side left \
		-padx 2
    pack .p.t -side top -pady 4 -padx 4

    frame .p.c
    checkbutton .p.c.on -text "always on top"     -variable temp_tick_ontop
    checkbutton .p.c.rd -text "round to hundreds" -variable temp_tick_round
    pack .p.c.on .p.c.rd -side left -padx 10
    pack .p.c -side top

    frame .p.f 
    frame .p.f.f
    label .p.f.f.l2 -text "security symbols:" 
    button .p.f.f.look -text "symbol lookup..." -command symbol_lookup
    pack .p.f.f.l2 .p.f.f.look -side left -padx 20 -fill x
    pack .p.f.f -side top
    scrollbar .p.f.scr -command ".p.f.symbols yview" -orient vertical
    text .p.f.symbols -width 40 -height 4  -wrap word \
		-yscrollcommand ".p.f.scr set"
    pack .p.f.scr -side right -fill y
    pack .p.f.symbols -side left -fill both -expand 1
    pack .p.f -side top -pady 4

    frame .p.b
    button .p.b.ok  -text " Ok "     -command {set doParms 1}
    button .p.b.can -text " Cancel " -command {set doParms 0}
    button .p.b.ex  -text " Exit TclTicker "   -command {Exit}
    pack .p.b.ok .p.b.can .p.b.ex -side left -padx 10 -pady 5
    pack .p.b -side top

    # fill inital services
    eval .p.q.service insert end $service_list
    .p.q.service selection set [lsearch -exact $service_list $service]

    # fill font choices
    .p.t.fon insert end Courier Helvetica Times
    if {[set idx [lsearch -exact "Courier Helvetica Times" $tick_font]] < 0} {
	set idx 1
    }
    .p.t.fon selection set $idx
    .p.t.fon see $idx

    # fill size choices
    .p.t.siz insert end 8 10 12 14 16 18
    if {[set idx [lsearch -exact "8 10 12 14 16 18" $tick_size]] < 0} {
	set idx 2
    }
    .p.t.siz selection set $idx
    .p.t.siz see $idx

    # fill initial symbols
    set i 0
    while {[info exists ticker_symbols([incr i])]} {
	.p.f.symbols insert end "$ticker_symbols($i)   "
    }

    set doParms 0
    wm deiconify .p
    focus .p
    raise .p
    update
    grab .p
    wm protocol .p WM_DELETE_WINDOW {set doParms 0}
    vwait doParms

    if {$doParms == 1} {
	set service [.p.q.service get [.p.q.service curselection]]
	set symbol_list [.p.f.symbols get 1.0 end]
	regsub -all "\[ \n\t\]\[ \n\t\]?" $symbol_list { } symbol_list
	set i 0
	unset ticker_symbols
	array set ticker_symbols {}
	foreach sym [split $symbol_list] {
	    if {[string length $sym] == 0} continue
	    set ticker_symbols([incr i]) $sym
	}
	set tick_speed $temp_tick_speed
	set tick_font  [.p.t.fon get \
	    [expr int(round([lindex [.p.t.fon yview] 0]*[.p.t.fon size]))]]
	set tick_size  [.p.t.siz get \
	    [expr int(round([lindex [.p.t.siz yview] 0]*[.p.t.siz size]))]]
	set tick_ontop $temp_tick_ontop
	set tick_round $temp_tick_round
	if {$tick_ontop} {
	    raise .
	}
	set refresh    $temp_refresh
	set_last_time

	if {[string length [string trim $temp_proxy_host]]} { 
	    set proxy_host [string trim $temp_proxy_host]
	    if {[string length [string trim $temp_proxy_port]] && \
		[scan $temp_proxy_port %d j] == 1} {
	        set proxy_port $j
	    } else {
	        set proxy_port ""
	    }
	} else {
	    set proxy_host ""
	    set proxy_port ""
	}
        http::config -proxyhost "$proxy_host" -proxyport "$proxy_port"

	if {[string length [string trim $temp_mail_host]]} { 
	    set mail_host [string trim $temp_mail_host]
	    if {[string length [string trim $temp_mail_port]] && \
		[scan $temp_mail_port %d j] == 1} {
	        set mail_port $j
	    } else {
	        set mail_port ""
	    }
	} else {
	    set mail_host ""
	    set mail_port ""
	}

	write_defaults
	refresh_data restart
	set index 0

    }  else {
       # restore tick_ontop
       set tick_ontop $save_tick_ontop
    }

    catch {
      bind .t <Button-1> get_parms
      bind .t <Button-2> get_parms
      bind .t <Button-3> get_parms
    }

    destroy .p
    catch {destroy .l}
}


proc symbol_lookup {} {
    global service proxy_host proxy_port
    catch {destroy .l}
    toplevel .l -class Dialog
    wm title .l "TclTicker: Symbol Lookup"

    if {[string length $proxy_host]} {
	set proxy $proxy_host
	if {[string length $proxy_port]} {
	    append proxy : $proxy_port
	}
    } else {
	set proxy (none)
    }

    label .l.l1 -text "lookup service: $service\n\
		using http proxy: $proxy"
    pack .l.l1  -side top -fill y
    label .l.msg -text ""
    pack .l.msg -side top -fill y

    frame .l.s
    label .l.s.l1 -text "search:"
    entry .l.s.sym -width 20
    button .l.s.go -text search -command do_lookup
    bind .l.s.sym <Key-Return> do_lookup
    pack .l.s.l1 .l.s.sym .l.s.go -side left -padx 4
    pack .l.s -side top -pady 4
    
    label .l.l2 -text "results"
    pack .l.l2 -side top

    frame .l.r
    scrollbar .l.r.v -orient vertical   -command ".l.r.res yview"
    scrollbar .l.r.h -orient horizontal -command ".l.r.res xview"
    listbox .l.r.res -width 35 -height 10 \
		    -exportselection 0 -selectmode multiple \
		    -yscrollcommand ".l.r.v set" \
		    -xscrollcommand ".l.r.h set" 
    pack .l.r.h -side bottom -fill x
    pack .l.r.v -side right -fill y
    pack .l.r.res -side left -expand 1 
    pack .l.r -side top 

    frame .l.b
    button .l.b.ok -text "Add selections" -command add_symbols
    button .l.b.can -text Cancel -command {destroy .l}
    pack .l.b.ok .l.b.can -side left -padx 10
    pack .l.b -side top -pady 4 
    grab .l
    focus .l.s.sym
    update
    tkwait window .l
}


proc do_lookup {} {
    global service
    catch {
      .l.msg configure -text "searching..."
      update idletasks
      set str [string trim [.l.s.sym get]]
      set symco_list ""
      set count 0
      if {! [catch {set symco_list [${service}_lookup $str]} res] } {
	  foreach {sym co} $symco_list {
	      .l.r.res insert end "$sym   $co"
	      incr count
	  }
      }
      if {$count} {
          .l.msg configure -text "search complete"
      } else {
          .l.msg configure -text "nothing found"
      }
    }
}

proc add_symbols {} {
    catch {
      set sel_list [.l.r.res curselection]
      set count 0
      foreach idx $sel_list {
	  incr count
	  set sym [lindex [split [.l.r.res get $idx]] 0]
	  .p.f.symbols insert end "  $sym "
      }
      .l.r.res selection clear 0 end
      if {$count} {
	  .l.msg configure -text "selections added"
      }
    }
}

proc dataChanged {var idx op} {
	global data
	global folio

	set skipTsx -1
	switch $op {
		w {
			# Match on the *,delta index because it is the last one set in the code.
			# By the time that index value is set, all other calculations will be
			# correct. TODO This is code-style dependent and should be fixed.
			if {[regexp {([^,]+),([0-9]+),delta} $idx idx symbol tsx]} {
				if {$tsx != $folio($symbol,highestTsx)} {
					return
				}
			} else {
				return
			}
		}
		u {
			# Remove the transactions from the list but make sure that the value
			# of the highest transaction is changed accordingly (if needed)
			
			if {[regexp {([^,]+),([0-9]+),delta} $idx idx symbol tsx]} {
				set l [lsort [array names folio "$symbol,*,delta"]]
				set high [lindex [split [lrange $l end end] ,] 1]
				set skipTsx $tsx
				if {$folio($symbol,highestTsx) > $high} {
					set folio($symbol,highestTsx) $high
				}
			} else {
				return
			}
		}
		default {
			return
		}
	}
	
	set qty 0
	set purchase 0
	set value 0
	set delta 0
	set daily 0
	# The next two catch statements will make sure nothing happens if
	# transactions are removed.
	for {set i 0} {$i <= $folio($symbol,highestTsx)} {incr i} {
		if {$i != $skipTsx} {
			catch {
				set qty [expr {$qty+$folio($symbol,$i,qty)}]
				set purchase [expr {$purchase+$folio($symbol,$i,purchase)}]
				set value [expr {$value+$folio($symbol,$i,qty)*$data($symbol,last)}]
				set delta [expr {$delta+$folio($symbol,$i,value)-$folio($symbol,$i,purchase)}]
				set daily [expr {$daily+$data($symbol,delta)*$folio($symbol,$i,qty)}]
			}
		}
	}
	
	set folio($symbol,summary,qty) $qty
	set folio($symbol,summary,purchase) [format "%.2f" $purchase]
	set folio($symbol,summary,value) [format "%.2f" $value]
	set folio($symbol,summary,delta) [format "%.2f" $delta]
	set folio($symbol,summary,daily) [format "%.2f" $daily]
}

proc Exit {} {
	write_folio
	exit
}

# start it up!

set_defaults

if {[string match *setup* [string tolower [lindex $argv 0]]]} {
    set setup 1
} else {
    set setup 0
}

if {! [read_defaults] || $setup} {
    # first time, or no tclticker file, get parms, or want 'setup'
    wm withdraw .

    if {! $setup} {
        set msg "Welcome to Stock Watcher!  This appears to be the first time\n\
        you have started Stock Watcher.  Please review these settings, and click\n\
        \"Ok\" to save your settings."
    } else {
        set msg ""
    }

    get_parms $msg
}

read_folio
trace variable folio uw dataChanged
start_ticker
wm deiconify .
raise .
update		;# get ticker going while waiting on inital data
refresh_data

