
#
proc APSWaveformAdjusterHelp {} {
    return "Usage: APSWaveformAdjuster widget \n\[-parent <string>\]\t\n\[-noPack 1\]\t\n\[-packOption <list>\]\t\n\[-title <string>\]\t\n\[-xlabel <label>\]\t\n\[-ylabel <label>\]\t\n\[-backgroundColor <color>\]\t\n\[-foregroundColor <color>\]\t\n\[-axisColor <color>\]\t\n\[-plotBackgroundColor <color>\]\t\n\[-plotForegroundColor <color>\]\t\n\[-selectionColor <color>\]\t\n\[-xmin <real>\]\t\n\[-xmax <real>\]\t\n\[-ymin <real>\]\t\n\[-ymax <ymax>\]\t\n\[-width <integer>\]\t\n\[-height <integer>\]\t\n\[-leftmargin <pixels>\]\t\n\[-pixelSize <integer>\]\t\n\[-waveformVariable <variable>\]\t\n\[-handleSpacing <integer>\]\t\n\[-selectedYCoordVar <variable>\]\t\n\[-selectedXCoordVar <variable>\]\t\n\[-static 1\]\t\n\[-autoscale 1\]\t\n\[-positionVariable <variable>\]\t\n\[-gridPack <list>\]\t\n\[-contextHelp <string>\]\t\n\nCreates\t\$parent\$widget.\n\tgraph\n\tycoord.label\n\tycoord.entry\n\t"
}


proc APSWaveformAdjuster {widget args} {
    if {![llength [info commands blt::bgexec]]} {
	package require BLT
    }

    global apsContextHelp
    set parent ""
    set noPack 0
    set packOption "-side top"
    set title ""
    set xlabel ""
    set ylabel ""
    set backgroundColor black
    set foregroundColor white
    set axisColor white
    set plotBackgroundColor \#737373
    set plotForegroundColor white
    set selectionColor red
    set xmin ""
    set xmax ""
    set ymin ""
    set ymax ""
    set width 300
    set height 300
    set pixelSize 4
    set gridPack ""
    set waveformVariable ""
    set showPrevious 0
    set handleSpacing 1
    set contextHelp ""
    set selectedYCoordVar ""
    set selectedXCoordVar ""
    set static 0
    set autoscale 0
    set leftmargin 0
    set positionVariable apsWaveformPosition
    APSStrictParseArguments {parent noPack packOption title xlabel ylabel backgroundColor foregroundColor axisColor plotBackgroundColor plotForegroundColor selectionColor xmin xmax ymin ymax width height pixelSize gridPack waveformVariable showPrevious handleSpacing contextHelp selectedYCoordVar selectedXCoordVar static autoscale positionVariable leftmargin}
    
    if {$contextHelp != ""} {
        set apsContextHelp($parent$widget) $contextHelp
    }
    if {$parent == ""} {
        toplevel $widget -bd 2
        wm title $widget $title
    } else {
        frame $parent$widget -bd 2
        if {!$noPack} {
            if {[string length $gridPack]} {
                eval grid $parent$widget $gridPack
            } else {
                eval pack $parent$widget $packOption
            }
        }
    }
    global $waveformVariable ${waveformVariable}Previous
    set xMax [expr {[llength [set $waveformVariable]] - 1}]
    if {![llength $xmax]} {
        set xmax $xMax
    }
    if {![llength $xmin]} {
        set xmin 0
    }
    if {![llength $ymin]} {
        set ymin [lindex [lsort -real [set $waveformVariable]] 0]
    }
    if {![llength $ymax]} {
        set ymax [lindex [lsort -real [set $waveformVariable]] end]
    }
    if {$ymin == $ymax} {
        set ymin [expr {$ymin - 1}]
        set ymax [expr {$ymax + 1}]
    }
    for {set n 0} {$n <= $xMax} {incr n} {
        lappend xdata $n
    }
    blt::graph $parent$widget.graph -cursor crosshair -width $width -height $height -bg $backgroundColor -fg $foregroundColor -title $title -plotbackground $plotBackgroundColor -leftmargin $leftmargin
    $parent$widget.graph xaxis configure -color $axisColor -background $backgroundColor -min $xmin -max $xmax -title $xlabel -titlecolor $axisColor
    $parent$widget.graph yaxis configure -color $axisColor -background $backgroundColor -min $ymin -max $ymax -title $ylabel -titlecolor $axisColor
    $parent$widget.graph legend configure -hide yes
    $parent$widget.graph grid configure -hide no -dashes "2 2"
    $parent$widget.graph element create line1 -xdata $xdata -ydata [set $waveformVariable] -color $plotForegroundColor -pixels 1 -linewidth 1
    if {$showPrevious} {
        package require math
        set ${waveformVariable}Previous [set ${waveformVariable}]
        $parent$widget.graph element create linePrevious -xdata $xdata -ydata [set ${waveformVariable}Previous] -color yellow -pixels 1 -linewidth 1 -dashes {5 10 5} -hide yes
    }
    if {$handleSpacing < 1} {
        set handleSpacing 1
    }
    for {set n 0} {$n <= $xMax} {incr n $handleSpacing} {
        lappend xdataHandle $n
        lappend ydataHandle [lindex [set $waveformVariable] $n]
    }
    if {[lindex $xdataHandle end] != $xMax} {
        lappend xdataHandle $xMax
        lappend ydataHandle [lindex [set $waveformVariable] end]
    }
    $parent$widget.graph element create line2 -xdata $xdataHandle -ydata $ydataHandle -color $plotForegroundColor -pixels $pixelSize -linewidth 0 -fill ""
    $parent$widget.graph element create selection -xdata [lindex $xdata 0] -ydata [lindex [set $waveformVariable] 0] -color $selectionColor -pixels $pixelSize -linewidth 0 -hide true
    $parent$widget.graph element create zoom1 -xdata "0 1" -ydata "1 1" -color $selectionColor -pixels 1 -linewidth 1 -hide true
    $parent$widget.graph element create zoom2 -xdata "0 0" -ydata "1 0" -color $selectionColor -pixels 1 -linewidth 1 -hide true
    $parent$widget.graph element create zoom3 -xdata "1 1" -ydata "1 0" -color $selectionColor -pixels 1 -linewidth 1 -hide true
    $parent$widget.graph element create zoom4 -xdata "1 0" -ydata "0 0" -color $selectionColor -pixels 1 -linewidth 1 -hide true
    pack $parent$widget.graph -fill both -expand true
    
    if {[llength $selectedYCoordVar] && [llength $selectedXCoordVar]} {
        APSLabeledEntry .xcoord -parent $parent$widget -label "Selected X Coord:" -type real -textVariable $selectedXCoordVar
        APSLabeledEntry .ycoord -parent $parent$widget -label "Selected Y Coord:" -type real -textVariable $selectedYCoordVar
        bind $parent$widget.graph <ButtonPress-1> "APSWaveformAdjusterSelect %W %x %y $positionVariable $selectedYCoordVar $selectedXCoordVar"
        bind $parent$widget.graph <ButtonRelease-1> "APSWaveformAdjusterZoom %W %x %y"
        bind $parent$widget.graph <ButtonPress-2> "APSWaveformAdjusterLocator %W %x %y $positionVariable  $selectedYCoordVar $selectedXCoordVar"
        bind $parent$widget.graph <ButtonRelease-2> "APSWaveformAdjusterSelectorAdd %W %x %y $positionVariable"
        if {!$static} {
            bind $parent$widget.ycoord.entry <Return> "APSWaveformAdjusterSetY $parent$widget.graph $waveformVariable $selectedYCoordVar $handleSpacing $autoscale"
            bind $parent$widget.graph <B2-Motion> "APSWaveformAdjusterLocator %W %x %y $positionVariable  $selectedYCoordVar $selectedXCoordVar"
            bind $parent$widget.graph <B1-Motion> "APSWaveformAdjusterMotion %W %x %y $waveformVariable $selectedYCoordVar 1 $handleSpacing $autoscale"
        }
    } else {
        bind $parent$widget.graph <ButtonPress-1> "APSWaveformAdjusterSelect %W %x %y $positionVariable {} {}"
        bind $parent$widget.graph <ButtonRelease-2> "APSWaveformAdjusterSelectorAdd %W %x %y $positionVariable"
        if {!$static} {
            bind $parent$widget.graph <B1-Motion> "APSWaveformAdjusterMotion %W %x %y $waveformVariable {} 1 $handleSpacing $autoscale"
        }
    }
    trace variable $waveformVariable w "APSWaveformAdjusterUpdate $parent$widget.graph $handleSpacing"
}

proc APSWaveformAdjusterUpdate {W handleSpacing waveformVariable tmp op} {
    global $waveformVariable ${waveformVariable}Previous
    set ydata1 [$W element cget line1 -ydata]
    if {[llength $ydata1] == [llength [set $waveformVariable]]} {
        if {[info exists ${waveformVariable}Previous]} {
            set t1 [eval math::max [set ${waveformVariable}Previous]]
            set b1 [eval math::min [set ${waveformVariable}Previous]]
            set t2 [eval math::max [set $waveformVariable]]
            set b2 [eval math::min [set $waveformVariable]]
            if {[expr {$t1 - $b1}] >= [expr {$t2 - $b2}]} {
                set color green
            } else {
                set color red
            }
            $W element configure linePrevious -hide no -color $color -ydata [set ${waveformVariable}Previous]
            set ${waveformVariable}Previous [set $waveformVariable]
        }
        $W element configure line1 -ydata [set $waveformVariable]
        set ydata2 ""
        for {set i 0} {$i < [llength [set $waveformVariable]]} {incr i $handleSpacing} {
            lappend ydata2 [lindex [set $waveformVariable] $i]
        }
        if {[expr {[llength [set $waveformVariable]] % $handleSpacing}] != 0} {
            lappend ydata2 [lindex [set $waveformVariable] end]
        }
        $W element configure line2 -ydata $ydata2
        $W element configure selection -hide true
        set xmin [expr {round([$W xaxis cget -min])}]
        set xmax [expr {round([$W xaxis cget -max])}]
        set sorted [lsort -real [lrange [set $waveformVariable] $xmin $xmax]]
        set ymin [lindex $sorted 0]
        set ymax [lindex $sorted end]
        if {$ymin == $ymax} {
            set ymin [expr {$ymin - 1.0}]
            set ymax [expr {$ymax + 1.0}]
        }
        $W yaxis configure -min $ymin -max $ymax
    }
}

proc APSWaveformAdjusterSetY {W waveformVariable selectedYCoordVar handleSpacing autoscale} {
    if {![$W element cget selection -hide]} {
        global $selectedYCoordVar
        APSWaveformAdjusterMotion $W [$W element cget selection -xdata] [set $selectedYCoordVar] $waveformVariable $selectedYCoordVar 0 $handleSpacing $autoscale
    }
}

proc APSWaveformAdjusterLocator {W x y positionVariable selectedYCoordVar selectedXCoordVar} {
    global $positionVariable
    if {[$W element closest $x $y $positionVariable line1]} {
        if {[llength $selectedYCoordVar]} {
            global $selectedYCoordVar
            set $selectedYCoordVar [set ${positionVariable}(y)]
        }
        if {[llength $selectedXCoordVar]} {
            global $selectedXCoordVar
            set $selectedXCoordVar [set ${positionVariable}(x)]
        }
    } else {
        $W element configure selection -hide true
    }
}

proc APSWaveformAdjusterSelectorAdd {W x y positionVariable} {
    global $positionVariable
    if {[$W element closest $x $y $positionVariable line1]} {
        set xdata2 [$W element cget line2 -xdata]
        set ydata2 [$W element cget line2 -ydata]
        set i [lsearch -exact $xdata2 [set ${positionVariable}(x)]]
        if {$i == -1} {
            lappend xdata2 [set ${positionVariable}(x)]
            set xdata2 [lsort -real $xdata2]
            set i [lsearch -exact $xdata2 [set ${positionVariable}(x)]]
            set ydata2 [linsert $ydata2 $i [set ${positionVariable}(y)]]
            $W element configure line2 -xdata $xdata2 -ydata $ydata2

        }
    } else {
        $W element configure selection -hide true
    }
}

proc APSWaveformAdjusterSelect {W x y positionVariable selectedYCoordVar selectedXCoordVar} {
    global $positionVariable
    if {[$W element closest $x $y $positionVariable line2]} {
        set x [set ${positionVariable}(x)]
        set y [set ${positionVariable}(y)]
        $W element configure zoom1 -hide true
        $W element configure zoom2 -hide true
        $W element configure zoom3 -hide true
        $W element configure zoom4 -hide true
        $W element configure selection -hide false -xdata $x -ydata $y
        if {[llength $selectedYCoordVar]} {
            global $selectedYCoordVar
            set $selectedYCoordVar $y
        }
        if {[llength $selectedXCoordVar]} {
            global $selectedXCoordVar
            set $selectedXCoordVar $x
        }
    } else {
        $W element configure selection -hide true
        set x [$W axis invtransform x $x]
        set y [$W axis invtransform y $y]
        $W element configure zoom1 -xdata "$x $x" -ydata "$y $y" -hide false
        $W element configure zoom2 -xdata "$x $x" -ydata "$y $y" -hide false
        $W element configure zoom3 -xdata "$x $x" -ydata "$y $y" -hide false
        $W element configure zoom4 -xdata "$x $x" -ydata "$y $y" -hide false
        
    }
}

proc APSWaveformAdjusterMotion {W x y waveformVariable selectedYCoordVar transform handleSpacing autoscale} {
    if {![$W element cget selection -hide]} {
        set xdata1 [$W element cget line1 -xdata]
        set ydata1 [$W element cget line1 -ydata]
        set xdata2 [$W element cget line2 -xdata]
        set ydata2 [$W element cget line2 -ydata]
        set i [lsearch -exact $xdata2 [$W element cget selection -xdata]]
        if {$i == 0} {
            set xmin [lindex $xdata2 $i]
        } else {
            set xmin [lindex $xdata2 [expr {$i - 1}]]
        }
        if {$i == [expr {[llength $xdata2] - 1}]} {
            set xmax [lindex $xdata2 $i]
        } else {
            set xmax [lindex $xdata2 [expr {$i + 1}]]
        }
        if {$transform} {
            set yvalue [$W axis invtransform y $y]
        } else {
            set yvalue $y
        }
        set ydata2 [lreplace $ydata2 $i $i $yvalue]
        set i [lsearch -exact $xdata1 [$W element cget selection -xdata]]
        set imin [lsearch -exact $xdata1 $xmin]
        set imax [lsearch -exact $xdata1 $xmax]
        for {set n $imin} {$n < $i} {incr n} {
            set ydata1 [lreplace $ydata1 $n $n [expr {[lindex $ydata1 $imin] + (($yvalue - [lindex $ydata1 $imin]) * (1.0 * $n - $imin) / ($i - $imin))}]]
        }
        set ydata1 [lreplace $ydata1 $i $i $yvalue]
        for {set n $imax} {$n > $i} {incr n -1} {
            set ydata1 [lreplace $ydata1 $n $n [expr {[lindex $ydata1 $imax] + (($yvalue - [lindex $ydata1 $imax]) * (1.0 * $imax - $n) / ($imax - $i))}]]
        }
        $W element configure line1 -ydata $ydata1
        $W element configure line2 -ydata $ydata2
        $W element configure selection -hide false -ydata $yvalue
        if {[llength $waveformVariable]} {
            global $waveformVariable
            trace vdelete $waveformVariable w "APSWaveformAdjusterUpdate $W $handleSpacing"
            set $waveformVariable $ydata1
            global ${waveformVariable}Previous
            if {[info exists ${waveformVariable}Previous]} {
                set t1 [eval math::max [set ${waveformVariable}Previous]]
                set b1 [eval math::min [set ${waveformVariable}Previous]]
                set t2 [eval math::max [set $waveformVariable]]
                set b2 [eval math::min [set $waveformVariable]]
                if {[expr {$t1 - $b1}] >= [expr {$t2 - $b2}]} {
                    set color green
                } else {
                    set color red
                }
                $W element configure linePrevious -hide no -color $color -ydata [set ${waveformVariable}Previous]
                set ${waveformVariable}Previous [set $waveformVariable]
            }
            trace variable $waveformVariable w "APSWaveformAdjusterUpdate $W $handleSpacing"
        }
        if {[llength $selectedYCoordVar]} {
            global $selectedYCoordVar
            set $selectedYCoordVar $yvalue
        }
        if {$autoscale} {
            set xmin [expr {round([$W xaxis cget -min])}]
            set xmax [expr {round([$W xaxis cget -max])}]
            set sorted [lsort -real [lrange [set $waveformVariable] $xmin $xmax]]
            set ymin [lindex $sorted 0]
            set ymax [lindex $sorted end]
            if {$ymin == $ymax} {
                set ymin [expr {$ymin - 1.0}]
                set ymax [expr {$ymax + 1.0}]
            }
            if {[expr {$ymax - $ymin}] < 1.0} {
                set ymax [expr {$ymin + 1.0}]
            }
            $W yaxis configure -min $ymin -max $ymax
        }
    } elseif {![$W element cget zoom1 -hide]} {
        set x [$W axis invtransform x $x]
        set y [$W axis invtransform y $y]
        set xstart [lindex [$W element cget zoom1 -xdata] 0]
        set ystart [lindex [$W element cget zoom1 -ydata] 0]
        $W element configure zoom1 -xdata "$xstart $x" -ydata "$ystart $ystart" -hide false
        $W element configure zoom2 -xdata "$xstart $xstart" -ydata "$ystart $y" -hide false
        $W element configure zoom3 -xdata "$x $x" -ydata "$ystart $y" -hide false
        $W element configure zoom4 -xdata "$xstart $x" -ydata "$y $y" -hide false
    }
}

proc APSWaveformAdjusterZoom {W x y} {
    if {![$W element cget zoom1 -hide]} {
        set x [$W axis invtransform x $x]
        set y [$W axis invtransform y $y]
        set xstart [lindex [$W element cget zoom1 -xdata] 0]
        set ystart [lindex [$W element cget zoom1 -ydata] 0]
        if {($x > $xstart) && ($ystart > $y)} {
            set ydata [lsort -real [lrange [$W element cget line1 -ydata] [expr {round($xstart)}] [expr {round($x)}]]]
            set ymin [lindex $ydata 0]
            set ymax [lindex $ydata end]
            if {$ymin == $ymax} {
                set ymin [expr {$ymin - 1.0}]
                set ymax [expr {$ymax + 1.0}]
            }
            $W xaxis configure -min $xstart -max $x
            $W yaxis configure -min $ymin -max $ymax
        } else {
            set xdata [$W element cget line1 -xdata]
            set ydata [lsort -real [$W element cget line1 -ydata]]
            $W xaxis configure -min [lindex $xdata 0] -max [lindex $xdata end]
            set ymin [lindex $ydata 0]
            set ymax [lindex $ydata end]
            if {$ymin == $ymax} {
                set ymin [expr {$ymin - 1.0}]
                set ymax [expr {$ymax + 1.0}]	    
            }
            if {[expr {$ymax - $ymin}] < 1.0} {
                set ymax [expr {$ymin + 1.0}]
            }
            $W yaxis configure -min $ymin -max $ymax
        }
        $W element configure zoom1 -hide true
        $W element configure zoom2 -hide true
        $W element configure zoom3 -hide true
        $W element configure zoom4 -hide true
    }
}
