#!/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

APSApplication . -name APSULatticeAndSetpoints -version 0.1 \
  -overview "Provides translation back and forth between APS-U lattice parameters and power supply setpoints."

set startArea SR
set startFunction setpoints
set args $argv
if {[APSStrictParseArguments {startArea startFunction}]} {
    return -code error "usage: APSULatticeAndSetpoints \[-startArea {SR|BTS}\] \[-startFunction {setpoints|lattice}\]"
}

set width 1100
set height 650

set standardizationTemplate(SR) /home/helios/oagData/sr/magnetConditioning/standardize.config
set standardizationTemplate(BTS)  /home/helios/oagData/bts/magnetConditioning/standardizeDwell.config

proc myDisableWidget { widget } {
    foreach child [winfo children $widget] {
        if [regexp .tn $child] {
            ttk::style configure $child -state disabled
        }
        catch {$child configure -state disabled}
        myDisableWidget $child
    }
    if [regexp .tn $widget] {
        ttk::style configure $widget -state disabled
    }
    catch {$widget configure -state disabled}
}

proc myEnableWidget { widget } {
    foreach child [winfo children $widget] {
        if [regexp .tn $child] {
            ttk::style configure $child -state normal
        }
        catch {$child configure -state normal}
        myEnableWidget $child
    }
    if [regexp .tn $widget] {
        ttk::style configure $widget -state normal
    }
    catch {$widget configure -state normal}
}

proc MakeActivityFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    global width height
    set widgetList [APSTabFrame $widget -parent $parent -label "" \
                      -labelList [list "Generate setpoints" "Generate lattice"] \
                      -width $width -height $height ]
    $parent$widget.frame configure -relief flat
    pack $parent$widget -side top
    $parent$widget.frame.tn select 0
    return $widgetList
}

proc MakeAreaFrame {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    global width height
    set widgetList [APSTabFrame $widget -parent $parent -label "" \
                      -labelList [list "SR" "BTS"] -width $width -height $height ]
    $parent$widget.frame configure -relief flat
    pack $parent$widget -side top
    $parent$widget.frame.tn select 0
    return $widgetList
}

set status "Working."
set statusCallback ""
set abortVariable ""

APSScrolledStatus .status -parent .userFrame -textVariable status -width 110

set energyGeV 6.0
APSLabeledEntry .energy -parent .userFrame -label "Energy (GeV): " -textVariable energyGeV \
  -type real

set activityFrameList [MakeActivityFrame .activity -parent .userFrame]
foreach frame $activityFrameList {
    eval lappend areaFrameList [MakeAreaFrame .area -parent $frame]
}

set innerWidget(SR,setpoints) [lindex $areaFrameList 0]
set innerWidget(SR,lattice)   [lindex $areaFrameList 2]
set innerWidget(BTS,setpoints) [lindex $areaFrameList 1]
set innerWidget(BTS,lattice)   [lindex $areaFrameList 3]

foreach area {SR BTS} {
    set setpointFile($area) ""
    set standardizationFile($area) ""
    set latticeFile($area) ""
    set adjustmentModel($area) None
}
set referenceSnapshotFile(SR) /home/helios/oagData/SCR/snapshots/SR/SR-UserBeamPreferred.gz
set referenceParameterFile(SR) $OAGGlobal(SRLatticesDirectory)/default/aps.param

proc MakeSRCheckButtonFrames {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global SRComponentsToInclude
    foreach type {Q1 Q2 Q3 Q6 Q7 SQ* Q4 Q5 Q8 M3 M4 S1 S2 S3} {
        set SRComponentsToInclude($type) 1
    }
    APSCheckButtonFrame .cb1 -parent $parent -label "Quadrupoles: " \
      -buttonList "Q1 Q2 Q3 Q6 Q7 SQ*" -variableList [list SRComponentsToInclude(Q1) SRComponentsToInclude(Q2) SRComponentsToInclude(Q3) SRComponentsToInclude(Q6) SRComponentsToInclude(Q7) SRComponentsToInclude(SQ*)] \
      -allNone 1 -orientation horizontal
    APSCheckButtonFrame .cb2 -parent $parent -label "Dipoles: " \
      -buttonList "Q4 Q5 Q8 M3 M4" -variableList [list SRComponentsToInclude(Q4) SRComponentsToInclude(Q5) SRComponentsToInclude(Q8) SRComponentsToInclude(M3) SRComponentsToInclude(M4)] \
      -allNone 1 -orientation horizontal
    APSCheckButtonFrame .cb3 -parent $parent -label "Sextupoles: " \
      -buttonList "S1 S2 S3" -variableList [list SRComponentsToInclude(S1) SRComponentsToInclude(S2) SRComponentsToInclude(S3)] \
      -allNone 1 -orientation horizontal
}

proc MakeBTSCheckButtonFrames {args} {
    set parent ""
    APSStrictParseArguments {parent}
    global BTSComponentsToInclude
    foreach type {Dipoles Quadrupoles} {
        set BTSComponentsToInclude($type) 1
    }
    APSCheckButtonFrame .cb1 -parent $parent -label "Magnets: " \
      -buttonList [array names BTSComponentsToInclude] -variableList [list BTSComponentsToInclude(Dipoles) BTSComponentsToInclude(Quadrupoles)] \
      -allNone 1 -orientation horizontal
}

set latticeFile(BTS) ""
set setpointFile(BTS) ""
set standardizationFile(BTS) ""

proc FillSetpointsFrameForArea {args} {
    set widget ""
    set area ""
    if {[APSStrictParseArguments {widget area}]==-1} {
        APSSetVarAndUpdate status "Invalid arguments specifed in FillSetpointsFrameForArea"
    }

    global setpointFile latticeFile standardizationFile referenceSnapshotFile referenceParameterFile adjustmentModel

    # User needs to provide a lattice file, output file, and select which magnets to configure
    APSLabeledEntry .setpoint -parent $widget -label "Lattice parameter file (input): " -textVariable latticeFile($area) \
      -width 80 -fileSelectButton 1 -fileSelectDirectory 0 -fileSelectValidity 1

    if [string compare $area "SR"]==0 {
        APSRadioButtonFrame .rb -parent $widget -label "Adjustment model: " \
            -variable adjustmentModel($area) -buttonList "None Delta-K1" -valueList "None Delta-K1" -orientation horizontal \
            -commandList [list "myDisableWidget $widget.ref" "myEnableWidget $widget.ref"]
        APSFrame .ref -parent $widget
        APSLabeledEntry .refLattice -parent $widget.ref.frame -label "Reference parameter file (input): " \
            -textVariable referenceParameterFile($area) -fileSelectPath [file dirname $referenceParameterFile($area)] \
            -width 80 -fileSelectButton 1 -fileSelectDirectory 0 -fileSelectValidity 1
        APSLabeledEntry .refSetpoint -parent $widget.ref.frame -label "Reference snapshot file (input): " \
            -textVariable referenceSnapshotFile($area) -fileSelectPath [file dirname $referenceSnapshotFile($area)] \
            -fileSelectPattern "${area}*.gz" \
            -width 80 -fileSelectButton 1 -fileSelectDirectory 0 -fileSelectValidity 1
        myDisableWidget $widget.ref
        MakeSRCheckButtonFrames -parent $widget
    } elseif [string compare $area "BTS"]==0 {
        MakeBTSCheckButtonFrames -parent $widget
    } else {
        APSSetVarAndUpdate status "Invalid area \"$area\" specifed in FillSetpointsFrameForArea"
    }

    APSLabeledEntry .output1 -parent $widget -label "Setpoint file (output): " -textVariable setpointFile($area) \
      -width 80 -fileSelectButton 1 -fileSelectDirectory 0 -fileSelectValidity 0
    APSLabeledEntry .output2 -parent $widget -label "Standardization file (output): " -textVariable standardizationFile($area) \
      -width 80 -fileSelectButton 1 -fileSelectDirectory 0 -fileSelectValidity 0
   
    APSFrame .buttons -parent $widget -packOption "-side top -fill x" -relief flat
    APSButton .autoname -parent $widget.buttons.frame -text "Auto name" -command "AutoName -outputType setpoints -area $area" \
      -packOption "-side left"
    APSButton .run -parent $widget.buttons.frame -text "Run" -command "GenerateSetpoints -area $area" \
      -packOption "-side left"
    APSButton .restore -parent $widget.buttons.frame -text "Restore" -command "RestoreSetpoints -area $area" \
      -packOption "-side left"
}

proc RestoreSetpoints {args} {
    set area ""
    if {[APSStrictParseArguments {area}]==-1} {
        return 0
    }
    global SRComponentsToInclude BTSComponentsToInclude setpointFile latticeFile standardizationFile standardizationTemplate
    set setpointFile($area) [string trim $setpointFile($area)]
    if ![string length $setpointFile($area)] {
        APSSetVarAndUpdate status "No setpoint file given for $area"
        return 0
    }
    if ![file exists $setpointFile($area)] {
        APSSetVarAndUpdate status "Setpoint file $setpointFile($area) not found for $area"
        return 0
    }
    APSSetVarAndUpdate status "Restoring values from $setpointFile($area)..."
    if [catch {exec sddscasr -restore $setpointFile($area)} result] {
        APSSetVarAndUpdate status "$result"
    } else {
        APSSetVarAndUpdate status Done.
    }
}

proc CheckValuesForSetpointGeneration {args} {
    set area ""
    if {[APSStrictParseArguments {area}]==-1} {
        return 0
    }
    global SRComponentsToInclude BTSComponentsToInclude setpointFile latticeFile standardizationFile standardizationTemplate
    if ![string length $latticeFile($area)] {
        APSSetVarAndUpdate status "No lattice parameter file given for $area"
        return 0
    }
    if ![file exists $latticeFile($area)] {
        APSSetVarAndUpdate status "Not found: $latticeFile($area)"
        return 0
    }
    if {![string length $setpointFile($area)] && ![string length $standardizationFile($area)]} {
        APSSetVarAndUpdate status "Give at least one of the two output filenames prior to running."
        return 0
    }
    if [string length $setpointFile($area)] {
        if [file exists $setpointFile($area)] {
            if {[APSMultipleChoice .yesno -question "$setpointFile($area) exists." \
                     -labelList "Overwrite Cancel" -returnList "1 0"]==0} {
                APSSetVarAndUpdate status "Setpoint file generation canceled. Choose another filename."
                return 0
            }
            file delete $setpointFile($area)
        }
    }
    if [string length $standardizationFile($area)] {
        if [file exists $standardizationFile($area)] {
            if {[APSMultipleChoice .yesno -question "$standardizationFile($area) exists." \
                     -labelList "Overwrite Cancel" -returnList "1 0"]==0} {
                APSSetVarAndUpdate status "Standardization file generation canceled. Choose another filename."
                return 0
            }
            file delete $standardizationFile($area)
        }
    }
    return 1
}

proc AutoName {args} {
    set area ""
    set outputType ""
    if {[APSStrictParseArguments {area outputType}]==-1} {
        return 0
    }
    global SRComponentsToInclude BTSComponentsToInclude setpointFile latticeFile standardizationFile standardizationTemplate
    switch $outputType {
        setpoints {
            if [string length $latticeFile($area)]==0 {
                APSSetVarAndUpdate status "Give a lattice parameter filename first."
                return 0
            }
            set setpointFile($area) [file rootname $latticeFile($area)].setp
            set standardizationFile($area) [file rootname $latticeFile($area)].std
        }
        lattice {
            # Actually this isn't used since the setpoint file is from SCR
            if [string length $setpointFile($area)]==0 {
                APSSetVarAndUpdate status "Give a lattice parameter filename first."
                return 0
            }
            set latticeFile($area) [file rootname $setpointFile($area)].param
        }
        default {
            APSSetVarAndUpdate status "outputType argument invalid in AutoName"
            return 0
        }
    }
    return 1
}

proc MakeElementMatchOption {args} {
    set area ""
    set fieldName ElementName
    if {[APSStrictParseArguments {area fieldName}]==-1} {
        return
    }
    #APSSetVarAndUpdate status "MakeElementMatchOption: area=$area"

    set matchOption ""
    global SRComponentsToInclude BTSComponentsToInclude
    switch $area {
        SR {
            set matchCount 0
            set matchList [list ${fieldName}=S*:FH?* ${fieldName}=S*:FV?* ${fieldName}=S*:H1* $fieldName=S*:V1*]
            foreach component [array names SRComponentsToInclude] {
                #APSSetVarAndUpdate status "Checking $component"
                if $SRComponentsToInclude($component) {
                    lappend matchList ${fieldName}=*:${component}*
                    incr matchCount
                }
            }
            switch $matchCount {
                0 {
                    APSSetVarAndUpdate status "Select some components to include in setpoint generation"
                    return ""
                }
                1 {
                    set matchOption -match=column,$matchList
                }
                default {
                    set matchOption -match=column,[lindex $matchList 0],[join [lrange $matchList 1 end] ",|,"],|
                }
            }
        }
        BTS {
            set matchList ""
            set matchCount 0
            if $BTSComponentsToInclude(Dipoles) {
                if [string compare $fieldName ControlName]==0 {
                    set matchCount 2
                    lappend matchList ${fieldName}=BTS:AB:* ${fieldName}=BTS:?B1:PS:*
                } else {
                    set matchCount 1
                    lappend matchList ${fieldName}=BTS:?B?
                }
            }
            if $BTSComponentsToInclude(Quadrupoles) {
                incr matchCount 2
                if [string compare $fieldName ControlName]==0 {
                    lappend matchList ${fieldName}=BTS:?Q?:PS:* ${fieldName}=BTS:?SQ?:PS:*
                } else {
                    lappend matchList ${fieldName}=BTS:?Q? ${fieldName}=BTS:?SQ?
                }
            }
            switch $matchCount {
                0 {
                    APSSetVarAndUpdate status "Select some components to include in setpoint generation"
                    return ""
                }
                1 {
                    set matchOption -match=column,$matchList
                }
                default {
                    set matchOption -match=column,[lindex $matchList 0],[join [lrange $matchList 1 end] ",|,"],|
                }
            }
        }
        default {
        }
    }
    #APSSetVarAndUpdate status "$matchOption"
    return $matchOption
}

proc GenerateSetpoints {args} {
    set area ""
    if {[APSStrictParseArguments {area}]==-1} {
        return
    }
    APSSetVarAndUpdate status "Generating setpoints for $area"
    
    global SRComponentsToInclude BTSComponentsToInclude setpointFile latticeFile energyGeV standardizationFile
    global standardizationTemplate adjustmentModel

    switch $area {
        SR {
            if [CheckValuesForSetpointGeneration -area $area]==0 {
                return 0
            }
            if [string length [set matchOption [MakeElementMatchOption -area $area]]]==0 {
                return 0
            }
            set tmpRoot /tmp/[APSTmpString]
            APSAddToTempFileList $tmpRoot.part $tmpRoot.sp
            #APSSetVarAndUpdate status "$matchOption"
            if [catch {exec sddsprocess $latticeFile($area) $tmpRoot.part $matchOption -nowarning} result] {
                APSSetVarAndUpdate status "error filtering lattice parameters for list of desired components: $result"
                return 0
            }
            set output $setpointFile($area)
            if ![string length $setpointFile($area)] {
                set output $tmpRoot.sp
            }
            catch {file delete $output}
            if [string compare $adjustmentModel($area) "Delta-K1"]==0 {
                global referenceParameterFile referenceSnapshotFile
                APSSetVarAndUpdate status "Working in Delta-K1 mode"
                APSLatticeToSetpointsDeltaK1 -input $tmpRoot.part -output $output -energyGeV $energyGeV \
                    -referenceParameters $referenceParameterFile($area) \
                    -referenceSnapshot $referenceSnapshotFile($area) \
                    -referenceEnergyGeV $energyGeV
            } else {
                APSSetVarAndUpdate status "Working in normal mode"
                APSLatticeToSetpoints -input $tmpRoot.part -output $output -energyGeV $energyGeV
            }
            if [string length $standardizationFile($area)] {
                  APSSetVarAndUpdate status "Generating standardization file"
                   exec sddsconvert $standardizationTemplate($area) -pipe=out -delete=column,Finish \
                        | sddsprocess -pipe -reprint=col,RealControlName,%s:PS:SetCurrentC,ControlName \
                        | sddsxref -pipe $output -nowarning -match=RealControlName=ControlName -take=ValueString \
                        | sddsprocess -pipe=in $standardizationFile($area) "-scan=column,Finish,ValueString,%le,units=A"
            }
            file delete $tmpRoot.part $tmpRoot.sp
            APSSetVarAndUpdate status "Done."
        }
        BTS {
            if [CheckValuesForSetpointGeneration -area $area]==0 {
                return 0
            }
            if [string length [set matchOption [MakeElementMatchOption -area $area]]]==0 {
                return 0
            }
            #APSSetVarAndUpdate status "$matchOption"
            set tmpRoot /tmp/[APSTmpString]
            APSAddToTempFileList $tmpRoot.part $tmpRoot.sp
            if [catch {exec sddsprocess $latticeFile($area) $tmpRoot.part $matchOption -nowarning} result] {
                APSSetVarAndUpdate status "error filtering lattice parameters for list of desired components: $result"
                return 0
            }
            set output $setpointFile($area)
            if ![string length $setpointFile($area)] {
                set output $tmpRoot.sp
            }
            catch {file delete $output}
            BTSLatticeToSetpoints -input $tmpRoot.part -output $output -energyGeV $energyGeV
            if [string length $standardizationFile($area)] {
                exec sddsconvert $standardizationTemplate($area) -pipe=out -delete=column,Finish,ElementName \
                    | sddsprocess -pipe -reprint=col,RealControlName,%s:PS:SetCurrentC,ControlName \
                                  -reedit=col,RealControlName,%/BTS:AB:PS:SetCurrentC/BTS:AB:CurrentAO/ \
                    | sddsxref -pipe $output -nowarning -match=RealControlName=ControlName -take=ValueString \
                    | sddsprocess -pipe=in $standardizationFile($area) "-scan=column,Finish,ValueString,%le,units=A"
            }
            file delete $tmpRoot.part $tmpRoot.sp
            APSSetVarAndUpdate status "Done."

            return 0
        }
        default {
            APSSetVarAndUpdate status "Unrecognized area \"$area\" in GenerateSetpoints"
        }
    }
    
}


proc GenerateLattice {args} {
    set area ""
    if {[APSStrictParseArguments {area}]==-1} {
        return
    }
    global SCRFile outputFileDir outputFileRoot energyGeV
    if ![string length $SCRFile($area)] {
        APSSetVarAndUpdate status "No SCR file chosen."
        return
    }
    if ![string length $outputFileDir($area)] {
        APSSetVarAndUpdate status "No output directory given."
        return
    }
    if ![string length $outputFileRoot($area)] {
        APSSetVarAndUpdate status "No output directory given."
        return
    }
    set output [file join $outputFileDir($area) $outputFileRoot($area)].param
    if [file exists $output] {
        if {[APSMultipleChoice .yesno -question "$output exists." \
                 -labelList "Overwrite Cancel" -returnList "1 0"]==0} {
            APSSetVarAndUpdate status "Parameter file generation canceled. Choose another filename."
            return
           }
        file delete $output
    }
    APSSetVarAndUpdate status "Generating lattice parameter file $output for $area from $SCRFile($area)"
    set matchOpt [MakeElementMatchOption -area $area -fieldName ControlName]
    #APSSetVarAndUpdate status "matchOpt: $matchOpt"
    APSAddToTempFileList $output.part
    if [string length $matchOpt]==0 {
        APSSetVarAndUpdate status "Nothing selected"
        return
    }
    if [catch {exec sddsprocess $SCRFile($area) $output.part $matchOpt} result] {
        return -code error "GenerateLattice: $result"
    }
    switch $area {
        SR {
            if [catch {APSSetpointsToLattice -input $output.part -output $output -energyGeV $energyGeV} result] {
                APSSetVarAndUpdate status "Problem: $result"
            }
        }
        BTS {
            if [catch {BTSSetpointsToLattice -input $output.part -output $output -energyGeV $energyGeV} result] {
                APSSetVarAndUpdate status "Problem: $result"
            }
        }
    }
    file delete $output.part 
    APSSetVarAndUpdate status "Done."
}


proc FillLatticeFrameForArea {args} {
    set widget ""
    set area ""
    if {[APSStrictParseArguments {widget area}]==-1} {
        APSSetVarAndUpdate status "Invalid arguments specifed in FillLatticeFrameForArea"
    }
    global setpointFile latticeFile
    set w $widget

    if [string compare $area "SR"]==0 {
        APSAddSCRDialog .scr -parent $widget -system $area -arrayName SRSCRFilename
        MakeSRCheckButtonFrames -parent $widget
    } elseif [string compare $area "BTS"]==0 {
        APSAddSCRDialog .scr -parent $widget -system Booster -arrayName BTSSCRFilename
        MakeBTSCheckButtonFrames -parent $widget
    } else {
        APSSetVarAndUpdate status "Invalid area \"$area\" specifed in FillSetpointsFrameForArea"
    }

    APSFrame .saveData -parent $w -label "Save data" \
      -contextHelp "Frame to specify the location of calculated data."
    
    APSLabeledEntry .outputFileDir -parent $w.saveData.frame -label "Directory: " \
      -textVariable outputFileDir($area) -width 90 \
      -contextHelp "Directory for lattice file."
    
    APSButton .daily -parent $w.saveData.frame.outputFileDir -packOption "-anchor e" \
      -text "daily"  -size small \
      -command "set outputFileDir($area) [APSGoToDailyDirectory]"

    APSLabeledEntry .outputFileRoot -parent $w.saveData.frame -label "Rootname: " \
      -textVariable outputFileRoot($area) -width 90 \
      -contextHelp "Root name for lattice file."

    APSFrame .buttons -parent $widget -packOption "-side top -fill x" -relief flat
    APSButton .run -parent $widget.buttons.frame -text "Run" -command "GenerateLattice -area $area" \
      -packOption "-side left"
}

foreach area {SR BTS} {
    FillSetpointsFrameForArea -widget $innerWidget($area,setpoints) -area $area
    FillLatticeFrameForArea -widget $innerWidget($area,lattice) -area $area
}

# L. Emery modified the tcl procedure APSAddSCRDialogCallback
# from library mplib/leutl/switch.tcl to
# add actions to the callback. This procedure has to be located
# after the call to MakeInputFrame because the mplib/leutl/switch.tcl 
# file is first sourced during within MakeInputFrame.
proc APSAddSCRDialogCallback {arrayName machine} {
    global $arrayName apsSCRSnapDir status 
    global outputFileRoot
    if [set ${arrayName}(FileNotFound)] {
        APSSetVarAndUpdate status "File not found!"
        return
    }
    set name [set ${arrayName}(Filename)]
    set descrip [set ${arrayName}(Description)]
    set snapDir $apsSCRSnapDir/$machine
    if ![file exists $snapDir/$name] {
        if [file exists $snapDir/${name}.gz] {
            set name ${name}.gz
        } else {
            APSSetVarAndUpdate status "File not found!"
            return
        }
    }
    set ${arrayName}(ShortFilename) $name
    set ${arrayName}(Filename) $snapDir/$name

    set machineToArea(SR) SR
    set machineToArea(Booster) BTS
    set area $machineToArea($machine)
    set outputFileRoot($area) [file root $name]

    global SCRFile 
    set SCRFile($area) $snapDir/$name
    APSSetVarAndUpdate status "File $snapDir/$name selected for $machine"
}

switch $startFunction {
    lattice {
        .userFrame.activity.frame.tn select 1
        switch $startArea {
            BTS {
                .userFrame.activity.frame.tn.f1.area.frame.tn select 1
            }
            default {
                .userFrame.activity.frame.tn.f1.area.frame.tn select 0
            }
        }
    }
    default {
        .userFrame.activity.frame.tn select 0
        switch $startArea {
            BTS {
                .userFrame.activity.frame.tn.f0.area.frame.tn select 1
            }
            default {
                .userFrame.activity.frame.tn.f0.area.frame.tn select 0
            }
        }
    }
}
