#!/bin/sh
# \
exec oagwish "$0" "$@"
set auto_path [linsert $auto_path 0 /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]
APSDebugPath

#
# Program to provide easy interface to sddsfft, sddsfdfilter, sddsdigfilter,
# and sddsnaff
#

proc setStatus {text} {
    global status
    set status "[clock format [clock seconds] -format %H:%M:%S]: $text"
    update
}

APSApplication . -name quickSDDSDSP -version 1.0 -overview "Allows applying SDDS digital signal processing programs to a dataset."

APSRenameExecToAPSBGExec

setStatus Ready...
APSScrolledStatus .status -parent .userFrame -textVariable status -width 65 -packOption "-fill x"

set filename ""
if [llength $argv] {
    set args $argv
    APSParseArguments {filename}
}

# Creates windows to allow user to select the columns for indep. and dep. variables

proc varSelect {args} {
    set arrayName ""
    set filenameVariable ""
    set type ""
    set slot ""
    set widget ""
    APSParseArguments {arrayName filenameVariable type slot widget}

    global $arrayName 
    global [os editstring S/(/Z) $filenameVariable]
    set $filenameVariable [string trim [string trimright [set $filenameVariable]]]
    
    if ![string length [set $filenameVariable]] {
        setStatus "Give a filename!"
        return
    }
    if [catch {APSGetSDDSNames -class column -fileName [set $filenameVariable]} varList] {
        setStatus "$varList"
        return
    } 
    set varname Independent
    set multiItem 0
    if [string match y $type] {
        set varname Dependent
        set multiItem 1
    } 
    set choiceList [APSChooseItemFromList -name "$varname variable" \
                      -itemList $varList -returnList $varList -returnIndices 0 \
                      -multiItem $multiItem]
    set $arrayName\($slot\) [join $choiceList ,]
    if {[string length $widget] && [string match x $type]} { 
        UpdateConversionWidget -fileName [set $filenameVariable] -arrayName $arrayName
    }
}
    
proc UpdateConversionWidget {args} {
    set fileName ""
    set arrayName ""
    APSStrictParseArguments {fileName arrayName}

    global $arrayName
    set widget [set $arrayName\(conversionWidget\)]
    APSDisableWidget $widget
    if [catch {APSGetSDDSNames -class column -fileName $fileName} varList] {
        setStatus "$varList"
        return
    } 
    if [catch {APSGetSDDSUnits -class column -fileName $fileName} unitsList] {
        setStatus "$unitsList"
        return
    }
    set index [lsearch -exact $varList [set $arrayName\(xVar\)]]
    set units [lindex $unitsList $index]
    set $arrayName\(xUnits\) $units
    if {[string compare $units "s"]==0 || [string compare $units "seconds"]==0} {
        # enable units conversion from seconds to other time units
        APSEnableWidget $widget
    } else {
        set $arrayName\(conversion\) none
    }
}

# Makes entry box and buttons for variable selection	

proc makeVariableFrame {widget args} {
    set filenameVariable ""
    set arrayName ""
    set parent ""
    APSStrictParseArguments {filenameVariable arrayName parent}
    APSFrame $widget -parent $parent -label Variables
    set vf $parent$widget.frame

    APSFrame .x -parent $vf -relief flat
    set w $vf.x.frame
    APSFrame .f1 -parent $w -relief flat
    global $arrayName
    APSLabeledEntry .xVar -width 30 -label "Independent variable:" \
      -textVariable $arrayName\(xVar\) -parent $w.f1.frame \
      -packOption "-side left" -contextHelp "Enter the name of the independent variable here \
	    or call up selection box with the button."
    APSButton .xselect -parent $w.f1.frame -packOption "-side right" -text "Select..." -command \
      "varSelect -arrayName $arrayName -slot xVar -type x -filenameVariable $filenameVariable -widget $w" \
      -contextHelp "Press to call up x variable selection box."
    APSFrame .f2 -parent $w -relief flat -packOption "-fill x"
    APSRadioButtonFrame .convert -parent $w.f2.frame -label "Conversion: " -orientation horizontal \
      -buttonList "none minutes hours days" -packOption "-side left" \
      -variable $arrayName\(conversion\) \
      -valueList "none minutes hours days" \
      -contextHelp "Select the units to express time quantities in."
    set $arrayName\(conversion\) none
    set $arrayName\(conversionWidget\) $w.f2
    APSDisableWidget $w.f2

    APSFrame .y -parent $vf -relief flat
    APSLabeledEntry .yVar -width 30 -label "  Dependent variables:" \
      -textVariable $arrayName\(yVar\) -parent $vf.y.frame \
      -packOption "-side left" -contextHelp "Enter the names of the dependent variables here or call up \
	    selection box with the button."
    APSButton .yselect -parent $vf.y.frame -packOption "-side right" -text "Select..." -command \
      "varSelect -arrayName $arrayName -slot yVar -type y -filenameVariable $filenameVariable" \
      -contextHelp "Press to call up y variable selection box."
}

proc AddPlotLayoutWidget {widget args} {
    set parent ""
    set variable ""
    APSStrictParseArguments {parent variable}

    APSRadioButtonFrame $widget -parent $parent -label "Plot layout: " \
      -orientation horizontal -variable $variable \
      -buttonList "1x1 1x2 2x1 2x2 3x3 4x4 5x5 6x6" \
      -valueList "-layout=1,1 -layout=1,2 -layout=2,1 -layout=2,2 -layout=3,3 -layout=4,4 -layout=5,5 -layout=6,6" \
      -contextHelp \
      "Selected the plot layout, which gives the number of plots horizontally and vertically on each panel."

}

proc AddFFTOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global FFT
    set FFT(padTruncate) ""
    set FFT(window) ""
    set FFT(normalize) ""
    set FFT(sampleInterval) 1
    set FFT(suppressAverage) ""
    set FFT(fullOutput) ""
    set FFT(psdOutput) ""
    set FFT(plotLayout) -layout=1,1
    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
      -commandButton 1 -textVariable FFT(outputFile) 
    APSLabeledEntry .sparse -parent $parent -label "Sparse data interval: " \
      -type integer -textVariable FFT(sampleInterval) \
      -contextHelp \
      "If the data is more finely sampled than you need, you may enter a sparsing interval here.  This results in decreasing the upper limit of the frequency spectrum."
    APSRadioButtonFrame .pad -parent $parent -orientation horizontal -variable FFT(padTruncate) \
      -buttonList "Truncate Pad Neither" -valueList {-truncate -padWithZeroes ""} \
      -label "Truncate or pad?" -contextHelp \
      "For datasets of length not equal to a product of small primes, you may get faster performance by padding with zeroes or truncating."
    APSRadioButtonFrame .window -parent $parent -orientation horizontal -variable FFT(window) -label "Window: " \
      -buttonList "Hanning Welch Parzen None" \
      -valueList {-window=hanning -window=welch -window=parzen ""} \
      -contextHelp \
      "Windowing can remove artifacts from the spectrum.  These appear when the signal is not exactly periodic with the sample window length."
    APSCheckButtonFrame .cb -parent $parent -label "Miscellaneous: " -orientation horizontal \
      -buttonList "Normalize SuppressAverage FullOutput PSDOutput" \
      -variableList "FFT(normalize) FFT(suppressAverage) FFT(fullOutput) FFT(psdOutput)" \
      -contextHelp \
      "If normalized, FFTs have maximum magnitude of 1.  Suppressing the average removes the DC term before the FFT is taken.  Full output include magnitudes and phases.  PSD output includes the power spectral density."
      
      
    set FFT(plotMode) -mode=y=log,y=spec
    APSRadioButtonFrame .plot -parent $parent -label "Plot style: " \
      -variable FFT(plotMode) -orientation horizontal \
      -buttonList "Lin-Log Log-Log Log-Lin" \
      -valueList "-mode=y=log,y=spec -mode=x=log,x=spec,y=log,y=spec -mode=x=log,x=spec" \
      -contextHelp \
      "Choose the style for plotting."
    AddPlotLayoutWidget .layout -parent $parent -variable FFT(plotLayout)

    APSButton .copy -parent $parent -text "Copy..." -command "CopySettings -to FFT"
    APSButton .fft -parent $parent -text "Run" -command "RunFFT" 
    APSButton .disp -parent $parent -text "Redisplay" -command "DisplayFFT" 
    set FFT(outputFile) ""
}

proc StartCommand {args} {
    set conversion ""
    set variable ""
    set input ""
    set program ""
    set oldUnits ""
    APSStrictParseArguments {conversion variable input program oldUnits}
    switch $conversion {
        minutes {
            set cFactor [expr 1./60.]
            set newUnits minute
        }
        hours {
            set cFactor [expr 1./60./60.]
            set newUnits hour
        }
        days {
            set cFactor [expr 1./60./60./24.0]
            set newUnits day
        }
        default {
            # Do this to ensure offsetting of the time variable
            set cFactor 1.0
            set newUnits $oldUnits
        }
    }
    set command "sddsprocess $input -pipe=out \"-redefine=column,$variable,$variable &$variable 0 \\\[ - $cFactor *,units=$newUnits\" "
    append command "| $program -pipe=in "
    return $command
}

proc RunFFT {} {
    global FFT
    if ![string length $FFT(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $FFT(outputFile)] {
        setStatus "Supply an output filename"
        return
    }
    if ![string length $FFT(xVar)] {
        setStatus "Choose an independent variable"
        return
    }
    if ![string length $FFT(yVar)] {
        setStatus "Choose one or more dependent variables"
        return
    }

    APSAddToRecentFileList -filename $FFT(inputFile)
    APSAddToRecentFileList -filename $FFT(outputFile)

    set command [StartCommand -conversion $FFT(conversion) -variable $FFT(xVar) \
                   -input $FFT(inputFile) -program sddsfft -oldUnits $FFT(xUnits) ]
    append command " -nowarning -column=$FFT(xVar),[join $FFT(yVar) ,] "
    if $FFT(sampleInterval)>1 {
        append command " -sampleInterval=$FFT(sampleInterval)"
    }
    foreach item {outputFile padTruncate window} {
        append command " $FFT($item)"
    }
    foreach item {normalize suppressAverage fullOutput psdOutput} {
        if [string length $FFT($item)] {
            append command " -$item"
        }
    }

    setStatus "$command"
    if [catch {eval exec $command} result] {
        setStatus "$result"
        return
    }
    DisplayFFT
}

proc DisplayFFT {} {
    global FFT
    APSExec -unixCommand \
      "sddsplot $FFT(plotLayout) -column=f,FFT* $FFT(outputFile) $FFT(plotMode) -separate"
}

proc AddNAFFOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global NAFF
    set NAFF(frequencies) 1
    set NAFF(truncate) ""
    set NAFF(presentation) Compact
    set NAFF(sort) Rootname
    set NAFF(plotLayout) -layout=2,2
    APSLabeledEntry .output -parent $parent -label "Output file: " -width 90 \
      -commandButton 1 -textVariable NAFF(outputFile) 
    APSLabeledEntry .frequencies -parent $parent -label "Frequencies: " -width 30 \
      -textVariable NAFF(frequencies) -type integer -contextHelp \
      "The number of frequencies to extract for each column."
    APSRadioButtonFrame .truncate -parent $parent -orientation horizontal -variable NAFF(truncate) \
      -buttonList "Yes No" -valueList {-truncate ""} \
      -label "Truncate: " -contextHelp \
      "For datasets of length not equal to a product of small primes, you may get faster performance by truncating."
    APSRadioButtonFrame .present -parent $parent -orientation horizontal -variable NAFF(presentation) \
      -label "Presentation: " \
      -buttonList {"Straight" "Compact"} \
      -valueList {"Straight" "Compact"} \
      -contextHelp \
      "Straight presentation shows the data as it appears in the output file.  If many columns were analyzed, this may be hard to read. In that case, try Compact."
    APSRadioButtonFrame .sort -parent $parent -orientation horizontal -variable NAFF(sort) \
      -label "Sort (compact presentation): " -limitPerRow 3 \
      -buttonList {"Alphanumeric" "Frequency" "Amplitude" "Phase" "Significance"} \
      -valueList {"Rootname" "Frequency" "Amplitude" "Phase" "Significance"} \
      -contextHelp \
      "For compact presentation mode, choose the quantity by which to sort."
      
    AddPlotLayoutWidget .layout -parent $parent -variable NAFF(plotLayout)
    APSButton .copy -parent $parent -text "Copy..." -command "CopySettings -to NAFF"
    APSButton .naff -parent $parent -text "Run" -command "RunNAFF" 
    APSButton .disp -parent $parent -text "Redisplay" -command "DisplayNAFF" 
    set NAFF(outputFile) ""
}

proc RunNAFF {} {
    global NAFF
    if ![string length $NAFF(inputFile)] {
        setStatus "Supply an input filename"
        return
    }
    if ![string length $NAFF(outputFile)] {
        setStatus "Supply an output filename"
        return
    }
    if ![string length $NAFF(xVar)] {
        setStatus "Choose an independent variable"
        return
    }
    if ![string length $NAFF(yVar)] {
        setStatus "Choose one or more dependent variables"
        return
    }
    if $NAFF(frequencies)<=0 {
        setStatus "Number of frequencies must be positive"
    }

    APSAddToRecentFileList -filename $NAFF(inputFile)
    APSAddToRecentFileList -filename $NAFF(outputFile)

    set command [StartCommand -conversion $NAFF(conversion) -variable $NAFF(xVar) \
                   -input $NAFF(inputFile) -program sddsnaff -oldUnits $NAFF(xUnits)]
    append command " -nowarning -column=$NAFF(xVar),[join $NAFF(yVar) ,] -terminateSearch=frequencies=$NAFF(frequencies)"
    foreach item {truncate} {
        append command " $NAFF($item)"
    }
    append command " -pipe | sddsprocess -pipe=in $NAFF(outputFile)"
    append command " -convertUnits=column,*Phase*,deg,,[expr 45.0/atan(1.0)]"
    setStatus "$command"
    if [catch {eval exec $command} result] {
        setStatus "$result"
        return
    }
    DisplayNAFF
}

proc DisplayNAFF {} {
    global NAFF
    set tmpFile /tmp/[APSTmpString]
    switch $NAFF(presentation) {
        Straight {
            exec sddsprintout -column $NAFF(outputFile) $tmpFile 
            set exportFile $NAFF(outputFile)
        }
        Compact {
            exec sddsconvert $NAFF(outputFile) -pipe=out -delete=param,OriginalPage \
              -delete=column,OriginalPage \
              | sddscollect -pipe -nowarning -collect=suffix=Frequency \
              -collect=suffix=Phase -collect=suffix=Amplitude -collect=suffix=Significance \
              | sddsprocess -pipe -filter=column,Frequency,0,1e300 \
              -delete=column,Units \
              | tee $NAFF(outputFile).cmpct \
              | sddssort -pipe -column=$NAFF(sort),incr \
              | sddsprintout -pipe=in -column $tmpFile 
            APSAddToRecentFileList -filename $NAFF(outputFile).cmpct
            set exportFile $NAFF(outputFile).cmpct
            exec sddsplot $NAFF(plotLayout) \
              -column=Frequency,Significance -column=Phase,Significance \
              -column=Amplitude,Significance -column=Frequency,Amplitude \
              $NAFF(outputFile).cmpct -separate -graph=symbol,scale=2 &
        }
    }
    APSFileDisplayWindow [APSUniqueName .] -fileName $tmpFile -deleteOnClose 1 \
      -width 120 -height 20 \
      -comment "NAFF of $NAFF(inputFile)" \
      -sddsExportableFile $exportFile \
      -contextHelp \
      "Displays NAFF analysis for $NAFF(inputFile).  Significance is the fractional signal amplitude left after removing the component.  It is 0 for a very significant component and 1 for an insignificant component."
}

proc AddFDFilterOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    label $parent.label1 -text "Frequency-domain filtering coming soon!"
    label $parent.label2 -text "Email borland@aps.anl.gov"
    pack $parent.label1
    pack $parent.label2
}

proc AddTDFilterOptions {args} {
    set parent ""
    APSStrictParseArguments {parent}
    label $parent.label1 -text "Time-domain filtering coming soon!"
    label $parent.label2 -text "Email borland@aps.anl.gov"
    pack $parent.label1
    pack $parent.label2
}

proc CopySettings {args} {
    set to ""
    APSStrictParseArguments {to}

    if ![string length $to] return
    global $to
    global tabLabelList
    set fromList ""
    foreach item $tabLabelList {
        if [string compare $item $to]==0 continue
        lappend fromList $item
    }
    set from [APSChooseItemFromList -name "Copy from where?" \
                -itemList $fromList -returnIndices 0 \
                -multiItem 0]
    if ![string length $from] return
    global $from
    foreach item {inputFile xVar yVar} {
        if ![info exists $to\($item\)] {
            continue
        }
        if ![info exists $from\($item\)] {
            continue
        }
        set $to\($item\) [set $from\($item\)]
    }

    UpdateConversionWidget -arrayName $to -fileName [set $to\(inputFile\)]

    foreach item {conversion} {
        if ![info exists $to\($item\)] {
            continue
        }
        if ![info exists $from\($item\)] {
            continue
        }
        set $to\($item\) [set $from\($item\)]
    }
    
}

proc FillTabFrame {widget args} {
    set type ""
    set filename ""
    APSStrictParseArguments {type filename}
    APSLabeledEntry .file -parent $widget -textVariable $type\(inputFile\) \
      -width 70 -commandButton 1 -label "Filename: " 
    makeVariableFrame .vf -parent $widget -filenameVariable $type\(inputFile\) \
      -arrayName $type
    if [string length $filename] {
        global $type
        set $type\(inputFile\) $filename
    }
    Add${type}Options -parent $widget
}


set tabLabelList [list FFT NAFF TDFilter FDFilter]
set tabWidgetList [APSTabFrame .main -parent .userFrame -label "" \
                     -labelList $tabLabelList -width 700 -height 500]
foreach tabWidget $tabWidgetList label $tabLabelList {
    FillTabFrame $tabWidget -type $label -filename $filename
}

