#---------------------------------------------------------------
# Where the user's keystrokes go
#---------------------------------------------------------------
# This widget doesn't even show up on the screen
set input ""
entry .entry -textvariable input
focus .entry

#---------------------------------------------------------------
# The interval timer---increments every second
#---------------------------------------------------------------
set now 0
set lasttime 0
after 1000 {clockintr}

proc clockintr {} {
    global now
    incr now
    after 1000 {clockintr}
}

set correct 0
set incorrect 0

# the ratio to "mix" older speed and new ones
#   higher means don't change so much
set MixRatio 0.6

# the "standard" speed in wpm
set StandardSpeed	100

# numbers of charcters per word
set CharPerWord	5

#---------------------------------------------------------------
# Procedure: pinput {# # #}
# Traces: input
#  Takes user's input and DTRT
#---------------------------------------------------------------
proc pinput {name element op} {
    global input keylistu keylists keylistpu keylistps \
	    prevchar prevcharu prevcharindex prevcharshifted \
	    showkeytop correct incorrect
    set inputindex [lsearch -exact "$keylistps" "$input"]
    if {$inputindex == -1} {
	set inputindex [lsearch -exact "$keylistpu" "$input"]
	set input [lindex "$keylistu" $inputindex]
	set inputshifted 0
    } else {
	set input [lindex "$keylists" $inputindex]
	set inputshifted 1
    }
    if {$inputindex != -1} {
	set inputu [lindex "$keylistu" $inputindex]
    } else {
	set inputu ""
    }
    if {$prevcharindex != -1} {
	.frame$prevcharindex configure -relief raised
	if [showkeytopp] {
	    .key$prevcharindex configure -text "$prevcharu"
	} else {
	    .key$prevcharindex configure -text ""
	}
    }
    if {$inputindex != -1} {
	.frame$inputindex configure -relief sunken
	.key$inputindex configure -text "$input"
    }
    # move forward if correct key
    if [equal [.text.text get insert] "$input"] {
	nextchar
	incr correct
	set iscorrect 1
    } else {
	incr incorrect
	set iscorrect 0
    }
    set prevcharindex $inputindex
    # skip multiple whitespaces
    if [regexp (\t|\n|\r|\ ) "$input"] {
	# if space was pressed while in the middle of a word, skip
	# to the beginning of the next word
	if {! $iscorrect} {
	    while {! [regexp (\t|\n|\r|\ ) [.text.text get insert]]} {
		if [.text.text compare insert == end] {
		    .text.text mark set insert 1.0
		    retag
		} else {
		    nextchar
		}
	    }
	}
	while {[regexp (\t|\n|\r|\ ) [.text.text get insert]]} {
	    if [.text.text compare insert+1c == end] {
		.text.text mark set insert 1.0
		retag
	    } else {
		nextchar
	    }
	}
    }
    set prevchar "$input"
    set prevcharu "$inputu"
    set prevcharindex $inputindex
    set prevcharshifted $inputshifted
    set input ""
    recalc
}
trace variable input w pinput
set prevchar ""
set prevcharu ""
set prevcharindex -1
set prevcharshifted 0

#---------------------------------------------------------------
# Procedure: equal {a b}
#  Returns 1 if two characters are equal, 0 otherwise.
#  Behavior depends on $ignorecases.
#---------------------------------------------------------------
proc equal {a b} {
    # a whitespace matches any whitespace
    if [regexp (\t|\n|\r|\ ) "$a"] {
	if [regexp (\t|\n|\r|\ ) "$b"] {
	    return 1
	} else {
	    return 0
	}
    }
    if [ignorecasesp] {
	return [expr {[string toupper "$a"] == [string toupper "$b"]}]
    } else {
	return [expr {"$a" == "$b"}]
    }
}

proc recalc {} {
    global now lasttime correct incorrect mistakes speed \
	    speedhappy mistakehappy MaxSmileys \
	    CharPerWord MixRatio StandardSpeed
    if {$now < $lasttime + 5} { return }
    set currspeed [expr $correct*60 / ($now-$lasttime) / $CharPerWord]
    set s [expr $speed * $MixRatio + $currspeed*(1-$MixRatio)]
    if {$s > $speed} {
	if {$speedhappy < $MaxSmileys} {
	    adjustsmileys .status.smileys.speed $speedhappy [expr $speedhappy+1]
	    incr speedhappy
	}
    } elseif {$s < $speed} {
	if {$speedhappy > -$MaxSmileys+1} {
	    adjustsmileys .status.smileys.speed $speedhappy [expr $speedhappy-1]
	    incr speedhappy -1
	}
    }
    set x [expr $currspeed + $StandardSpeed].0
    if { $correct == 0 } {
	# This is just going to be copied back down there
	set m $mistakes
    } elseif {$incorrect == 0} {
	set m [expr $mistakes * $StandardSpeed / $x]
    } else {
	if {$mistakes != 0} {
	    set m [expr exp(log($mistakes) * $StandardSpeed / $x \
		    + log($incorrect/$correct.0) * $currspeed / $x)]
	} else {
	    set m [expr exp(log(0.1) * $StandardSpeed / $x + \
		    log($incorrect/$correct.0) * $currspeed / $x)]
	}
    }
    if {$m == 0} {
	set $m 0.000001
    }
    set d $m
    for {set i [expr -$MaxSmileys+1]} {($d < 0.1) && ($i < $MaxSmileys)} \
	    {incr i} {set d [expr $d * 1.5]}
    adjustsmileys .status.smileys.mistakes $mistakehappy $i
    set mistakehappy $i
    set mistakes [format "%.3f" $m]
    set speed [format "%.0f" $s]
    set lasttime $now
    set correct 0
    set incorrect 0
}
