#!/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)]
APSStandardSetup

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

set SRDeviceStatusDebug 0

proc debugStatus {text} {
    global SRDeviceStatusDebug
    if !$SRDeviceStatusDebug return
    setStatus "$text"
}

proc CheckAddDevice {deviceName} {
    #
    # The purpose of this procedure is to add a device to the database.
    # It is only needed in the event that the suffixes file has changed.
    # Basically, as the user tries to add data for the new BPM, an entry is created.
    #
    global requiredFieldList defaultValueList deviceList orderedFullDeviceList 
    if [lsearch -exact $orderedFullDeviceList $deviceName]==-1 {
        return -code error "Error: $deviceName not in ordered list.  Seek expert help!"
    }
    if [lsearch -exact $deviceList $deviceName]!=-1 return
    debugStatus "CheckAddDevice adding $deviceName"
    foreach field $requiredFieldList default $defaultValueList {
        set $field\($deviceName\) $default
    }
}

proc ProcessSelectionChange {} {
    # This procedure is called when the user changes the device he is working on.
    # It's main job is to copy the data from the widgets back to the arrays for the
    # device that was just worked on, and to copy data for the new device-to-edit
    # the the working widgets.

    global deviceSelection system
    global deviceSelectionSaved 
    global SelectionWidget deviceList
    
    APSDisableWidget $SelectionWidget

    global requiredFieldList
    set lastDeviceName $deviceSelectionSaved
    set newDeviceName $deviceSelection 
    
    CheckAddDevice $newDeviceName
    set FieldsHaveChanged 0
    foreach field $requiredFieldList {
        global $field
        if [string compare [set $field\(0\)] [set $field\($lastDeviceName\)]]!=0 {
            set FieldsHaveChanged 1
        }
        set $field\($lastDeviceName\) [set $field\(0\)]
        if {[regexp {ID|BM} $lastDeviceName type b c] && $field=="PreampSerialNumber"} {
            regexp {:(.*)} $lastDeviceName a pv c
            #pv has to be P1 or P2, the xbpm preamplifier are the same for P1 and P2 at the same sector
            if {$pv=="P2"} {
                set otherPV [regsub {P2} $lastDeviceName P1]
            } else {
                set otherPV [regsub {P1} $lastDeviceName P2]
            }
            set ${field}($otherPV) [set ${field}($lastDeviceName)]
        }
        set $field\(0\) [set $field\($newDeviceName\)]
    }

    setStatus "$FieldsHaveChanged fields were changed for $lastDeviceName"
    if $FieldsHaveChanged { 
        global OriginalComment changedDeviceList
        lappend changedDeviceList $lastDeviceName
        if [string compare $Comments($lastDeviceName) $OriginalComment($lastDeviceName)]==0 {
            setStatus "WARNING: You changed $lastDeviceName but didn't change the comment.\nYou should always enter a new comment whenever you change a device's status."
            bell
        }
    }
    set OriginalComment\($lastDeviceName\) $Comments($lastDeviceName)

    set deviceSelectionSaved $deviceSelection

    APSEnableWidget $SelectionWidget
}

proc ChangeInputWidgetStates {args} {
    set state normal
    set plane ""
    APSStrictParseArguments {state plane}
    global inputFieldWidgetList inputFieldPlaneList
    foreach item $inputFieldWidgetList itemPlane $inputFieldPlaneList {
        if [string compare $itemPlane $plane] continue
        catch {$item configure -state $state}
    }
}

proc CheckConfigChanged {args} {
    set changedDeviceList ""
    APSParseArguments {changedDeviceList}
    
    if ![llength $changedDeviceList] {
        return
    }
    setStatus "$changedDeviceList"
    global ConfigMTime0 loadingDate dataDir 
    set mtime [GetConfigMTime]
    if {$mtime>$ConfigMTime0} {
        set tmpRoot /tmp/[APSTmpString]
        #set changeList [lsort [glob -nocomplain changeLog${loadingDate}*]]
	set currentDate [clock format $mtime  -format "%Y %j"]

	set changeList [APSFindFilesBetweenDates \
			    -rootname changeLog \
			    -directory $dataDir \
			    -startDateList $loadingDate\
			    -endDateList $currentDate]
	
        if ![llength $changeList] {
            return -code error "The config file was changed after loading, you need reload the configuration before saving."
        }
        APSAddToTmpFileList -ID SRStatus -fileList $tmpRoot.1
        if [catch {eval exec sddscombine $changeList -merge -pipe=out \
                       | sddssort -pipe -col=Time \
                       | sddsprocess -pipe "-filter=col,Time,0,$ConfigMTime0,!"  \
                       -nowarnings \
		       | sddsprocess -pipe -nowarnings "-filter=col,Time,0,$mtime" \
                       | sddssort -pipe=in -col=DeviceName -col=Time,decreasing -numericHigh  -unique \
                       $tmpRoot.1 } result] {
            return -code error $result
        }
        set rows [exec sdds2stream -rows=bar $tmpRoot.1]
        if !$rows {
            return -code error "No changes were logged, even though the config.sdds file changed."
        }
        if [catch {sdds load $tmpRoot.1 changeData} result] {
            return -code error $result
        }
        set deviceList [lindex $changeData(Column.DeviceName) 0]
        set timeList [lindex $changeData(Column.Time) 0]
        foreach device $changedDeviceList {
            set index [lsearch $deviceList $device]
            if [lsearch $deviceList $device]>=0 {
                set user [lindex [lindex $changeData(Column.User) 0] $index]
                set workstation [lindex [lindex $changeData(Column.Station) 0] $index]
                set timeStamp [lindex [lindex $changeData(Column.TimeStamp) 0] $index]
                set time [lindex $timeList $index]
                if {$time>$ConfigMTime0} {
                    return -code error "$device has been changed by $user on $workstation at $timeStamp"
                }
            }
        }
        unset changeData
        #update the fields of devices that were changed
        set option -match=col
        set first 1
        foreach device $deviceList { 
            if {!$first} {
                append option ,DeviceName=$device,||
            } else {
                append option ,DeviceName=$device
            }
            set first 0
        }
        global requiredFieldList
        setStatus "updating the changed devices: [join $deviceList ,]"
        foreach field $requiredFieldList {
            global $field
        }
        APSAddToTmpFileList -ID SRStatus -fileList $tmpRoot.2
        if [catch {exec sddsprocess config.sdds $option $tmpRoot.2} result] {
            return -code error $result
        }
        if [catch {sdds load $tmpRoot.2 newData} result] {
            return -code error $result
        }
        set index 0
        foreach device [lindex $newData(Column.DeviceName) 0] {
            foreach field $requiredFieldList {
                set $field\($device\) [lindex [lindex $newData(Column.$field) 0] $index]
            }
            set OriginalComment($device) $Comments($device)
            incr index
        }
        setStatus "updating done." 
	set loadingDate $currentDate
    }
}

proc SaveConfiguration {} {
    global requiredFieldList requiredFieldTypeList requiredFieldDescripList env
    global deviceName deviceList changedDeviceList assumeAllChanged
    global OriginalComment orderedFullDeviceList system ConfigMTime0
    global deviceSelection deviceSelectionSaved system
    
    
    set newDeviceName $deviceSelection 
    
    setStatus "[pwd]"
    if {![APSMultipleChoice [APSUniqueName .] \
            -question "Really? Save and install this configuration?" \
            -labelList "Yes No" -returnList "1 0"]} {
        return
    }

    foreach field $requiredFieldList {
        global $field
    }

    if !$assumeAllChanged {
        set FieldsHaveChanged 0
        foreach field $requiredFieldList {
            if [string compare $field Comments]!=0 {
                if [string compare [set $field\(0\)] [set $field\($newDeviceName\)]]!=0 {
                    setStatus "$field changed for $newDeviceName"
                    incr FieldsHaveChanged
                }
            }
        }
        
        if $FieldsHaveChanged {
            if [string compare $Comments(0) $OriginalComment($newDeviceName)]==0 {
                if {![APSMultipleChoice [APSUniqueName .] \
                        -question "WARNING: You changed $newDeviceName but didn't change the comment.\nYou should always enter a new comment whenever you change a device's status.  What do you want to do?" \
                        -labelList {"Continue" "Abort"} \
                        -returnList {1 0}]} {
                    return
                }
            }
            lappend changedDeviceList $newDeviceName
        } else {
            if [string compare $Comments(0) $OriginalComment($newDeviceName)]!=0 {
                lappend changedDeviceList $newDeviceName
            }
            set FieldsHaveChanged 1
        }
        
        debugStatus "Fields have changed for $newDeviceName"

        if [llength $changedDeviceList]==0 {
            setStatus "Nothing has changed, no save needed."
            return
        } else {
            
        }
    }
    if [catch {CheckConfigChanged -changedDeviceList $changedDeviceList} result] {
	APSAlertBox .warning -errorMessage "$result; status not changed - another user online." -type warning
        setStatus "$result"
	setStatus "$changedDeviceList status not changed - another user online."
        return
    }
    foreach field $requiredFieldList {
        set $field\($newDeviceName\) [set $field\(0\)]
    }
    set OriginalComment\($newDeviceName\) $Comments($newDeviceName)

    global combinedFilesOnly 
    setStatus "Saving combined file..."
    debugStatus "[llength $deviceList] devices present" 

    foreach field $requiredFieldList type $requiredFieldTypeList description $requiredFieldDescripList {
        set dataList ""
        # Use the ordered device list so the file isn't saved in some random order
        foreach device $orderedFullDeviceList {
            if [lsearch -exact $deviceList $device]!=-1 {
                lappend dataList [set $field\($device\)]
            }
        }
        set outputData(Column.$field) [list $dataList]
        set outputData(ColumnInfo.$field) "type $type description \"$description\""
    }
    
    set outputData(ColumnNames) $requiredFieldList
    set outputData(ParameterNames) "Time TimeStamp User Host"
    set outputData(ParameterInfo.Time) "type SDDS_LONG units s"
    set theTime [clock seconds]
    set outputData(Parameter.Time) [list $theTime]
    set outputData(Parameter.TimeStamp) [list [clock format $theTime -format %Y/%m/%d@%H:%M:%S]]
    global apsScriptUser apsScriptHost
    set outputData(Parameter.User) [list $apsScriptUser]
    set outputData(Parameter.Host) [list $apsScriptHost]
    
    set filename config-[clock format $theTime -format %Y-%j-%m%d-%H%M%S]
    if [catch {sdds save $filename outputData
        file delete -force config.sdds 
        exec ln -s $filename config.sdds} result] {
        unset outputData
        return -code error "SaveConfiguration: Unable to save file: $result"
    }
    set ConfigMTime0 $theTime
    unset outputData
    setStatus "Data saved to $filename."
    
    if $assumeAllChanged {
        if [catch {exec sdds2stream -column=DeviceName config.sdds} changedDeviceList] {
            return -code error "$changeDeviceList"
        }
    } else {
        set changedDeviceList [lsort -unique $changedDeviceList]
    }
    if [llength $changedDeviceList] {
        set timeStamp [clock format $theTime -format "%Y %m/%d %H:%M:%S"]
        regsub {config-} $filename "changeLog" filename1 
        if [catch {exec sddsmakedataset -pipe=out \
                       -col=DeviceName,type=string -data=[join $changedDeviceList ,] \
                       | sddsprocess -pipe=in $filename1 \
                       -print=col,User,$env(USER) \
                       -define=col,Time,$theTime,type=long \
                       "-print=col,TimeStamp,$timeStamp" } result] {
            setStatus "SaveConfiguration: Unable to create changeLog file: $result"
        } 
    }
    if !$combinedFilesOnly {
        setStatus "Saving individual files"
        
        foreach changedDevice $changedDeviceList {
            setStatus "Working on $changedDevice"
            if ![file exist $changedDevice] {
                exec mkdir -m 777 $changedDevice
            }
            if [catch {exec sddsprocess $filename $changedDevice/[os editstring Z- $filename] \
                           -match=column,DeviceName=$changedDevice -nowarnings} result] {
                return -code error "SaveConfiguration: $result"
            }
            if [string match "bpm*" $system] {
                if [catch {ShowHistoryBPMs -deviceName $changedDevice -commentsOnly 1 \
                               -display 0} result] {
                    return -code error "SaveConfiguration: $result"
                }
            }
        }
        setStatus "Data saved in [llength $changedDeviceList] individual files"
    }
    set changedDeviceList ""
    set assumeAllChanged 0
}

proc ReadConfiguration {args} {
    set interactive 0
    APSStrictParseArguments {interactive}
    global requiredFieldList requiredFieldTypeList 
    global deviceName  deviceList system
    global defaultValueList orderedFullDeviceList
    
    if $interactive {
        set configList [lsort -decreasing [glob -nocomplain config-????-???-????-??????]]
        set configFile [APSChooseItemFromList -name "Choose configuration" \
                          -itemList $configList -returnIndices 0]
        if ![string length $configFile] return
    } else {
        set configFile config.sdds
    }

    setStatus "Reading configuration..."
    if [catch {sdds load $configFile inputData} result] {
        setStatus "Unable to read configuration: $result"
        return
    }
    set exampleField [lindex $inputData(ColumnNames) 0]
    debugStatus "File has [llength [lindex $inputData(Column.$exampleField) 0]] rows"

    # Check if all "required" fields are in the configuration file.  If not,
    # insert the default value from the fields.sdds file for that column.
    foreach field $requiredFieldList default $defaultValueList {
        if [lsearch -exact $inputData(ColumnNames) $field]==-1 {
            setStatus "Warning: field $field not found in config.sdds"
            set inputData(Column.$field) [list [APSReplicateItem -item $default -number [llength [lindex $inputData(Column.$exampleField) 0]]]]
        }
        global $field
    }

    global OriginalComment
    set index 0
    foreach device [lindex $inputData(Column.DeviceName) 0] {
        foreach field $requiredFieldList {
            set $field\($device\) [lindex [lindex $inputData(Column.$field) 0] $index]
        }
        if [lsearch -exact $orderedFullDeviceList $device]==-1 {
            return -code error "Error: $device not in full ordered list.  Seek expert help!"
        }
        set OriginalComment($device) [lindex [lindex $inputData(Column.Comments) 0] $index]
        incr index
    }
    set deviceList [array names OriginalComment]
   
    setStatus "Configuration data for [llength $deviceList] devices loaded"

    global deviceSelection
   
    set newDeviceName $deviceSelection 
    
   
    foreach field $requiredFieldList {
        set $field\(0\) [set $field\($newDeviceName\)]
    }
    set deviceList $orderedFullDeviceList 
    unset inputData
    ProcessSelectionChange
}

proc ShowHistory {} {
    global system
    switch $system {
        bpms -
        bpmsTest -
        boosterbpms {
            ShowHistoryBPMs
        }
        vcorrectors -
        hcorrectors {
            ShowHistoryCorrectors
        }
    }
}

proc ShowCommentHistory {} {
    global system
    switch $system {
        bpms -
        bpmsTest {
            ShowHistoryBPMs -commentsOnly 1
        }
        vcorrectors -
        hcorrectors {
            ShowHistoryCorrectors -commentsOnly 1
        }
    }
}

proc ShowHistoryBPMs {args} {
    set commentsOnly 0
    set deviceName ""
    set display 1
    APSStrictParseArguments {commentsOnly deviceName display}
    
    global sectorSelection deviceTypeSelection system combinedFilesOnly system
    if ![string length $deviceName] {
        if [regexp {booster} $system] {
            set deviceName $sectorSelection$deviceTypeSelection 
        } else {
            set deviceName S$sectorSelection$deviceTypeSelection 
        }
    }
    if ![file exist $deviceName] {
        exec mkdir -m 777 $deviceName
    }
    if $combinedFilesOnly {
        set fileList [lsort [glob -nocomplain config-????-???-????-??????]] 
    } else {
        if [file exist $deviceName/combinedData.sdds] {
            set fileList $deviceName/combinedData.sdds
            eval lappend fileList [lsort [glob -nocomplain $deviceName/????-???-????-??????]]
        } else {
            set fileList [lsort [glob -nocomplain $deviceName/????-???-????-??????]]
        }
    }
    # for some reason sdsdquery always return a "child killed: write on
    # pipe with no readers" error so I add readAll option.
    catch {eval exec sddscombine $fileList -pipe=out \
               | sddsquery -col -pipe=in -readAll} existingColumnList
    set existingColumnList [split $existingColumnList]

    # The file fields may have new names not included in existingColumnList 
    if [catch {sdds load fields.sdds fd} result] {
        return -code error "ShowHistoryBPMs: $result"
    }
    set option1 -print=column,Key,
    set option2 ""
    set names [lindex $fd(Column.Name) 0]
    set types [lindex $fd(Column.Type) 0]
    set excludedList [list timestamp time user host numbercombined filename devicename pagenumber]
    foreach name $names type $types {
        if {-1==[lsearch -exact $existingColumnList $name]} {
            continue
        }
        set nm [string tolower $name]
        if {[lsearch -exact $excludedList $nm]==-1} {
            if {[string compare $type string]==0} {
                set option1 ${option1}-%s
            } elseif {[string compare $type character]==0} {
                set option1 ${option1}-%c
            } elseif {[string compare $type long]==0} {
                set option1 ${option1}-%ld
            } elseif {[string compare $type short]==0} {
                set option1 ${option1}-%hd
            } elseif {[string compare $type double]==0 || [string compare $type float]==0} {
                set option1 ${option1}-%.6g
            } else {
                continue
            }
            set option2 ${option2},$name
        }
    }
    set option ${option1}${option2}

    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [llength $fileList] {
        if {[catch {eval exec sddscombine $fileList -pipe=out \
                        | sddsprocess -pipe \
                        -match=column,DeviceName=$deviceName -nowarning \
                        $option \
                        | sddsexpand -pipe -nowarning \
                        | sddscollapse -pipe \
                        | sddsbreak -pipe -changeOf=Key \
                        | sddsprocess -pipe=in $tmpFile -clip=1,0,invert \
                        \"-print=column,Comment,%20s %10s %s,TimeStamp,User,Comments\"} result]} {
            puts $result
            return -code error "ShowHistoryBPMs(1): $result"
        }
        set tmpFileP /tmp/[APSTmpString]
        APSAddToTempFileList $tmpFileP
        
        if $commentsOnly {
            if [catch {exec sddscombine $tmpFile -pipe=out -merge \
			   | sddsprocess -pipe -reedit=col,Comments,%/\n// \
                           | tee $deviceName/comments.sdds \
                           | sddsprintout -pipe=in $tmpFileP -column=Comment -nolabel \
                           "-title=Comment history for $deviceName as of [clock format [clock seconds]]\n" } result] {
                return -code error "ShowHistoryBPMs(2): $result"
            }
        } else {
            if [catch {exec sddsexpand $tmpFile -pipe=out -nowarning \
                           | sddsprintout -pipe=in $tmpFileP -format=double=%5.0f,float=%5.0f -postPageLines=1 \
                           "-title=Status history for $deviceName as of [clock format [clock seconds]]\n" \
                           -parameter=TimeStamp,format=%20s -parameter=User,format=%10s \
                           -parameter=DeviceType,format=%10s,endsline \
                           -parameter=NonexistentH \
                           -parameter=NonexistentV,endsline \
                           -parameter=ResponseEquationH \
                           -parameter=ResponseEquationV,endsline \
                           -parameter=OkFor*H,edit=%/OkFor//ei/?/,endsline \
                           -parameter=OkFor*V,edit=%/OkFor//ei/?/,endsline \
                           -parameter=ButtonNameH -parameter=ButtonNameV,endsline \
                           -parameter=Comments,format=%30s} result] {
                return -code error "ShowHistoryBPMs(3): $result"
            }
        }
        if $display {
            APSFileDisplayWindow [APSUniqueName .] -comment "History for $deviceName" \
                -fileName $tmpFileP -deleteOnClose 1 -width 130 -height 40 -defaultButtons 1
        }
    }
}

proc ShowHistoryCorrectors {args} {
    set commentsOnly 0
    APSStrictParseArguments {commentsOnly}

    global sectorSelection deviceTypeSelection system
    if [regexp {booster} $system] {
        set deviceName $sectorSelection$deviceTypeSelection 
    } else {
        set deviceName S$sectorSelection$deviceTypeSelection
    }
    set fileList [lsort [glob -nocomplain config-????-???-????-??????]] 

    setStatus "Processing [llength $fileList] files..."

    # for some reason sdsdquery always return a "child killed: write on
    # pipe with no readers" error so I add readAll option.
    catch {eval exec sddscombine $fileList -pipe=out \
               | sddsquery -col -pipe=in -readAll} existingColumnList
    set existingColumnList [split $existingColumnList]

    # The file fields may have new names not included in existingColumnList 
    if [catch {sdds load fields.sdds fd} result] {
        return -code error "$result"
    }
    set option1 -print=column,Key,
    set option2 ""
    set names [lindex $fd(Column.Name) 0]
    set types [lindex $fd(Column.Type) 0]
    set excludedList [list timestamp time user host numbercombined filename devicename pagenumber]
    foreach name $names type $types {
        if {-1==[lsearch -exact $existingColumnList $name]} {
            continue
        }
        set nm [string tolower $name]
        if {[lsearch -exact $excludedList $nm]==-1} {
            if {[string compare $type string]==0} {
                set option1 ${option1}-%s
            } elseif {[string compare $type character]==0} {
                set option1 ${option1}-%c
            } elseif {[string compare $type long]==0} {
                set option1 ${option1}-%ld
            } elseif {[string compare $type short]==0} {
                set option1 ${option1}-%hd
            } elseif {[string compare $type double]==0 || [string compare $type float]==0} {
                set option1 ${option1}-%.6g
            } else {
                continue
            }
            set option2 ${option2},$name
        }
    }
    set option ${option1}${option2}

    set tmpFile /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFile
    if [catch {eval exec sddscombine $fileList -pipe=out \
                 | sddsprocess -pipe -match=column,DeviceName=$deviceName -nowarning \
                 $option \
                 | sddsexpand -pipe -nowarning \
                 | sddscollapse -pipe \
                 | sddscombine -merge -pipe \
                 | sddsbreak -pipe -changeOf=Key \
                 | sddsprocess -pipe=in $tmpFile -clip=1,0,invert -nowarning} result] {
        return -code error "ShowHistoryCorrectors: $result"
    }

    set tmpFileP /tmp/[APSTmpString]
    APSAddToTempFileList $tmpFileP
    if $commentsOnly {
        if [catch {exec sddscombine $tmpFile -pipe=out -merge \
                       | sddsprocess -pipe \
                       "-print=column,Comment,%20s %10s %s,TimeStamp,User,Comments" \
                       | sddsprintout -pipe=in \
                       $tmpFileP -column=Comment -nolabel \
                       "-title=Comment history for $deviceName as of [clock format [clock seconds]]\n" } result] {
            return -code error "ShowHistoryCorrectors: $result"
        }
    } else {
        if [catch {exec sddsexpand $tmpFile -pipe=out -nowarning \
                       | sddsprintout -pipe=in \
                       $tmpFileP -format=double=%5.0f,float=%5.0f \
                       -postpagelines=2 \
                       "-title=Status history for $deviceName as of [clock format [clock seconds]]\n" \
                       -parameter=TimeStamp,format=%20s -parameter=User,format=%10s,endsline \
                       -parameter=Nonexistent,endsline \
                       -parameter=OkFor*,edit=%/OkFor//ei/?/,endsline \
                       -parameter=Comments,format=%30s} result] {
            return -code error "ShowHistoryCorrectors: $result"
        }
    }

    APSFileDisplayWindow [APSUniqueName .] -comment "History for $deviceName" \
      -fileName $tmpFileP -deleteOnClose 1 -width 130 -height 40 -defaultButtons 1
}

proc ShowOverview {} {
    global requiredFieldList deviceList orderedFullDeviceList

    set w [APSUniqueName .]
    set field [APSChooseItemFromList -name "Choose item to overview" \
      -itemList [concat None $requiredFieldList] -returnIndices 0]
    if [string compare $field None]==0 return
    global $field
    lappend dataList "$field values for all devices"
    foreach device $orderedFullDeviceList {
        if [lsearch -exact $deviceList $device]!=-1 {
            lappend dataList "[format %10s $device:] [set $field\($device\)]"
        }
    }
    APSScrolledListWindow [APSUniqueName .] \
      -name "$field values for all devices" \
      -itemList $dataList -printButton 1 -acceptButton 0 -emailButton 1
}

proc MakeSelectionWidgets {widget args} {
    set parent ""
    set system ""
    APSStrictParseArguments {parent system}

    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    global SelectionWidget 
    set SelectionWidget $w

    global deviceList labelList commandList
    
    global deviceSelection deviceSelectionSaved
    set deviceSelection [lindex $deviceList 0]
    set deviceSelectionSaved $deviceSelection
    if {$system=="bpms"} {
	set device1 ""
	set device2 ""
	foreach device $deviceList {
	    if [regexp "BTS" $device] {
		lappend device1 $device
		lappend comm1 ProcessSelectionChange
	    } else {
		lappend device2 $device
		lappend comm2 ProcessSelectionChange
	    }
	}
	APSRadioButtonFrame .s1 -parent $w -label "" -variable deviceSelection -buttonList $device1 \
	    -valueList $device1 -orientation horizontal -limitPerRow 6 \
	    -commandList $comm1
        $w.s1.frame configure -bd 0
	APSRadioButtonFrame .s2 -parent $w -label "" -variable deviceSelection -buttonList $device2 \
	    -valueList $device2 -orientation horizontal -limitPerRow 6 \
	    -commandList $comm2
        $w.s2.frame configure -bd 0

    } else {
        set deviceList [exec sdds2stream -col=Prefix prefixes.sdds]
        set tmpRoot /tmp/[APSTmpString]
        if [catch {exec sddsbreak prefixes.sdds -change=Group $tmpRoot.1 } result] {
            return -code error "Error processing devie list: $result"
        }
	APSAddToTmpFileList -ID btsstatus -fileList $tmpRoot.1
	set pages [exec sdds2stream -npages=bare $tmpRoot.1]
	for {set page 1} {$page<=$pages} {incr page} {
	    set devices [exec sdds2stream -page=$page -col=Prefix $tmpRoot.1]
	    set labels ""
	    set maxstr 11
	    foreach device $devices {
		set num [expr $maxstr - [string length $device]]
		set nm $device
		if $num>0 {
		    for {set i 0} {$i<$num} {incr i} {
			append nm " "
		    }
		}
		lappend labels $nm
		
	    }
	    set number [lindex [exec sdds2stream -page=$page -col=Buttons $tmpRoot.1] 0]
	    set comms [APSReplicateItem -item ProcessSelectionChange -number [llength $devices]]
	    set vertical 1
	    if $vertical {
		APSRadioButtonFrame .s$page -parent $w -label "" -variable deviceSelection -buttonList $labels \
		    -valueList $devices -orientation vertical -limitPerRow $number \
		    -commandList $comms -packOption "-side left"
	    } else {
		APSRadioButtonFrame .s$page -parent $w -label "" -variable deviceSelection -buttonList $labels \
		    -valueList $devices -orientation horizontal  -limitPerRow $number \
		    -commandList $comms 
	    }
	    $w.s$page.frame configure -bd 0
	}
    }
}

set disableCommentsTrace 0
proc UpdateComments {widget name1 name2 op} {
    if {$op == "write"} {
        global $name1 disableCommentsTrace
        if {$disableCommentsTrace == 0} {
            $widget delete 1.0 end
            $widget insert end [set $name1\($name2\)]
        }
    }
}

proc TypeComments {widget} {
    global Comments disableCommentsTrace
    set disableCommentsTrace 1
    set Comments(0) [string trimright [$widget get 1.0 end]]
    set disableCommentsTrace 0
}

proc UpdateXrayBPMPreamp {deviceName} {
    if [regexp {ID} $deviceName] {
        set type ID
    } elseif [regexp {BM} $deviceName] {
        set type BM
    } else {
        return
    }
    global PreampSerialNumber
    regexp {:(.*)} $deviceName a b c
    if {$b=="P1"} {
        set other [regsub {P1} $deviceName P2]
    } else {
        set other [regsub {P2} $deviceName P1]
    }
    set PreampSerialNumber($other) PreampSerialNumbe($deviceName)
}
proc MakeFieldWidgets {widget args} {
    set parent ""
    set system ""
    APSStrictParseArguments {parent system}

    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    pack [frame $w.top] -fill x
    pack [frame $w.bottom] -fill x
    pack [frame $w.bottom.planeH] -fill x -side left
    pack [frame $w.bottom.planeV] -fill x -side left -padx 50

    global requiredFieldList requiredFieldTypeList requiredFieldDescripList requiredFieldPlaneList
    global requiredFieldIsEnumList requiredFieldEnumsList
    set maxLength 0
    foreach field $requiredFieldList {
        if [string length $field]>$maxLength {
            set maxLength [string length $field]
        }
    }
    incr maxLength
    set count 0
    global inputFieldWidgetList deviceList inputFieldPlaneList
    set name [lindex $deviceList 0]
    
    set deviceName ""
    foreach field $requiredFieldList type $requiredFieldTypeList descrip $requiredFieldDescripList plane \
      $requiredFieldPlaneList isEnum $requiredFieldIsEnumList enumValues $requiredFieldEnumsList {
          global $field
          set $field\(0\) [set $field\($name\)]
          incr count
          switch $type {
              SDDS_STRING {
                  if $isEnum {
                      APSRadioButtonFrame .w$count -parent $w.top \
                        -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -contextHelp $descrip \
                        -valueList [split $enumValues ,] \
                        -buttonList [split $enumValues ,] \
                        -orientation horizontal 
                  } else {
                      if [lsearch -exact [list ResponseEquationH ResponseEquationV] $field]!=-1 {
                          APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -textVariable $field\(0\) -width 80 -contextHelp $descrip
                          lappend inputFieldWidgetList $w.top.w$count.entry
                          lappend inputFieldPlaneList $plane
                      } elseif [lsearch -exact [list Comments] $field]!=-1 {
                          pack [frame $w.top.w$count] -fill x
                          pack [label $w.top.w$count.label -text "[APSMenuFramePadLength $field: $maxLength] "] -side left
                          APSScrolledText .st -parent $w.top.w$count -height 3
                          $w.top.w$count.st.text insert end [set $field\(0\)]
                          
                          trace add variable $field\(0\) write "UpdateComments $w.top.w$count.st.text"
                          bindtags $w.top.w$count.st.text "Text . all $w.top.w$count.st.text"
                          bind $w.top.w$count.st.text <KeyPress> "TypeComments $w.top.w$count.st.text"
                          bind $w.top.w$count.st.text <ButtonRelease-2> "TypeComments $w.top.w$count.st.text"
                      } elseif [string compare $field DeviceName]==0 {
                          APSLabeledOutput .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -textVariable $field\(0\) -width 10 -contextHelp $descrip
                          set deviceName [set $field\(0\)]
                          pack configure $w.top.w$count.entry -side left
                      } else {
                          if {($plane == "H") || ($plane == "V")} {
                              APSLabeledEntry .w$count -parent $w.bottom.plane$plane -label "[APSMenuFramePadLength $field: $maxLength] " \
                                -textVariable $field\(0\) -width 10 -contextHelp $descrip
                              
                              pack configure $w.bottom.plane$plane.w$count.entry -side left
                              lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.entry
                          } else {
                              APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                                -textVariable $field\(0\) -width 10 -contextHelp $descrip
                              pack configure $w.top.w$count.entry -side left
                              lappend inputFieldWidgetList $w.top.w$count.entry
                          }
                          lappend inputFieldPlaneList $plane
                      }
                  }
              }
              SDDS_SHORT {
                  if [string match $field Nonexistent] {
                      APSRadioButtonFrame .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled" \
                           "ChangeInputWidgetStates -state normal"}
                  } elseif [string compare $field NonexistentH]==0 {
                      APSRadioButtonFrame .w$count -parent $w.bottom.planeH -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled -plane H" \
                           "ChangeInputWidgetStates -state normal -plane H"}
                  } elseif [string compare $field NonexistentV]==0 {
                      APSRadioButtonFrame .w$count -parent $w.bottom.planeV -label "[APSMenuFramePadLength $field: $maxLength] " \
                        -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                        -contextHelp $descrip -commandList \
                        {"ChangeInputWidgetStates -state disabled -plane V" \
                           "ChangeInputWidgetStates -state normal -plane V"}
                  } else {
                      if {($plane == "H") || ($plane == "V")} {
                          APSRadioButtonFrame .w$count -parent $w.bottom.plane$plane -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                            -contextHelp $descrip
                          lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.frame.button1
                          lappend inputFieldWidgetList $w.bottom.plane$plane.w$count.frame.button2
                      } else {
                          APSRadioButtonFrame .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                            -variable $field\(0\) -buttonList {Yes No} -valueList {1 0} -orientation horizontal \
                            -contextHelp $descrip
                          lappend inputFieldWidgetList $w.top.w$count.frame.button1
                          lappend inputFieldWidgetList $w.top$plane.w$count.frame.button2
                      }
                      lappend inputFieldPlaneList $plane
                      lappend inputFieldPlaneList $plane
                  }
              }
              SDDS_DOUBLE {
                  APSLabeledEntry .w$count -parent $w.top -label "[APSMenuFramePadLength $field: $maxLength] " \
                    -textVariable $field\(0\) -contextHelp $descrip
              }
          }
      }
}

proc MakeOperationWidgets {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    
    APSButton .write -parent $w -text "Save/Install" -command "SaveConfiguration" \
      -contextHelp "Saves configuration to disk and installs it as the default."
    APSButton .read -parent $w -text "Read..." -command "ReadConfiguration -interactive 1" \
      -contextHelp "Reads a configuration chosen from a list of available configurations."
    APSButton .overview -parent $w -text "Overview" -command "ShowOverview" \
      -contextHelp "Shows the values of a specific setting for all devices."
    APSButton .history -parent $w -text "History (Current device)" -command "ShowHistory" \
      -contextHelp "Shows the history of all settings for the current device.  The history is drawn from the existing configuration files and doesn't include the present state of values in this application."
    APSButton .chistory -parent $w -text "Comment history (Current device)" -command "ShowCommentHistory" \
      -contextHelp "Shows the history of all settings for the current device.  The history is drawn from the existing configuration files and doesn't include the present state of values in this application."
}

proc PreBuildApplication {args} {
    set system ""
    APSStrictParseArguments {system}
    APSApplication . -name ManageBTSDeviceStatus:$system  \
      -overview "Storage ring device status tracking tool"
    setStatus "Working..."
    APSScrolledStatus .status -parent .userFrame -textVariable status -width 88 \
      -height 5 -packOption "-fill both -expand true"
    update
}

proc BuildApplication {args} {
    set system ""
    APSStrictParseArguments {system}

    MakeSelectionWidgets  .sel -parent .userFrame -system $system
    MakeFieldWidgets  .fields -parent .userFrame -system $system
    MakeOperationWidgets .ops -parent .userFrame
    AddSpecialFeatures .sf -parent .userFrame -system $system 
    setStatus "Ready."
    update
}

proc AddSpecialFeatures {widget args} {
    set system ""
    set parent ""
    APSStrictParseArguments {system parent}

    switch $system {
        bpms {
            AddSpecialFeaturesBPMs $widget -parent $parent
        }
        vcorrectors -
        hcorrectors {
        }
    }
}
set statusWin 0
proc ViewStatus {args} {
    global statusWin system
   
    if !$statusWin {
        switch $system {
            hcorrectors {
                exec SRCorrStatusViewer -plane H &
            }
            vcorrectors {
                exec SRCorrStatusViewer -plane V &
            }
            bpms {
                exec SRBPMStatusViewer &
            }
            boosterbpms {
                setStatus "boostser bpm status viewer does not exist yet."
                return
            }
        }
    }
    set statusWin 1
    set tmpFile /tmp/[APSTmpString]
    switch $system {
        bpms {
            if [catch {exec sddsprocess config.sdds -pipe=out -match=col,ResponseEquationH=,! \
                         | sddsprintout -pipe=in $tmpFile.1 -col=DeviceName,format=%20s "-title=config.sdds status printout" \
                         -col=ResponseEquationH,format=%50s -col=ResponseEquationV,format=%50s -width=140 } result] {
                return -code error $result
            }
            if [catch {exec sddsprintout config.sdds $tmpFile.2  -col=DeviceName,format=%11s -width=140 \
                         -col=ElectronicsType,label=Electronics,format=%12s \
                         -col=DeviceType,format=%11s -col=BPLDInterfaceSerialNumber,label=BPLDNumber,format=%11s \
                         -col=BergozCalibrationType,label=BergozCal,format=%10s -col=BergozSerialNumber,label=BergozSerial,format=%12s \
                         -col=FilterSerialNumber,label=FilterNumber,format=%12s -col=PreampSerialNumber,label=PreampNumber,format=%12s \
                          } result] {
                return -code error $result
            }
            exec cat $tmpFile.1 $tmpFile.2 > $tmpFile.printout
        }
        hcorrectors -
        vcorrectors {
            if [catch {exec sddsprintout config.sdds $tmpFile.printout \
                         -col=DeviceName -col=CorrectorLimit,format=%10.1f } result] {
                return -code error $result
            }
        }

    }
   
    APSFileDisplayWindow [APSUniqueName .fileDisp] -fileName $tmpFile.printout -width 140 -height 50\
      -comment "Printout for config.sdds" -deleteOnClose 1 -font "courier 12" \
      -printCommand "enscript -r"
    
}
proc AddSpecialFeaturesBPMs {widget args} {
    set parent ""
    APSStrictParseArguments {parent}
    
    APSFrame $widget -parent $parent -label ""
    set w $parent$widget.frame
    
    APSButton .view -parent $w -text "View Installed Status" -command "ViewStatus" -contextHelp \
      "bring up binary status displayer and print out the text status from installed status configuration."
    APSButton .xBadBO -parent $w -text "Set Bad BO's" \
      -command "SetBPMBadBOValues" \
      -contextHelp "Sets the BadBO process variables from the \"ok for orbit correction\" data.  The data used is that in the currently loaded configuration.  Generally, you should save the configuration first as a matter of good practise."
   
}

proc ReadApplicationConfig {args} {
    set system ""
    APSStrictParseArguments {system}
    
    if ![file exists fields.sdds] {
        APSAlertBox [APSUniqueName .] -errorMessage "Not found: fields.sdds"
        exit 1
    }
    if [catch {sdds load fields.sdds configData} result] {
        APSAlertBox [APSUniqueName .] -errorMessage "$result"
        exit 1
    }
    global requiredFieldList requiredFieldTypeList requiredFieldDescripList \
        requiredFieldWidthList requiredFieldPlaneList requiredFieldEnumsList requiredFieldIsEnumList \
        defaultValueList sectorList
    set requiredFieldList [lindex $configData(Column.Name) 0]
    set requiredFieldDescripList [lindex $configData(Column.Description) 0]
    set requiredFieldWidthList [lindex $configData(Column.PrintoutWidth) 0]
    set defaultValueList [lindex $configData(Column.DefaultValue) 0]
    set requiredFieldTypeList ""
    foreach type [lindex $configData(Column.Type) 0] {
        lappend requiredFieldTypeList SDDS_[string toupper $type]
    }
    
    if [lsearch -exact $configData(ColumnNames) IsEnumeratedField]!=-1 {
        set requiredFieldIsEnumList [lindex $configData(Column.IsEnumeratedField) 0]
        set requiredFieldEnumsList  [lindex $configData(Column.EnumeratedValueList) 0]
    } else {
        set requiredFieldIsEnumList [APSReplicateItem -item 0 -number [llength $requiredFieldList]]
        set requiredFieldEnumsList  [APSReplicateItem -item "" -number [llength $requiredFieldList]]
    }
   
    set requiredFieldPlaneList [APSReplicateItem -item "" -number [llength $requiredFieldList]]
    
   
    # set defaults, mostly 
    global orderedFullDeviceList OriginalComment
    foreach field $requiredFieldList {
        global $field
    }
    set index 0
    set deviceList [exec sdds2stream -col=Prefix prefixes.sdds]
    
    foreach device $deviceList {
        lappend orderedFullDeviceList $device
        incr index
        foreach field $requiredFieldList defaultValue $defaultValueList {
            set $field\($device\) $defaultValue
        }
        set DeviceName($device) $device
        set Comments($device) ""
        set OriginalComment($device) ""
    }
    set name [lindex $deviceList 0]
    foreach field $requiredFieldList {
        set $field\(0\) [set $field\($name\)]
    }
    
}

proc SetBPMBadBOValues {} {
    global requiredFieldList deviceList newDeviceName system
    
    if {[lsearch -exact $requiredFieldList OkForDCOrbitCorrectionH]==-1 || \
          [lsearch -exact $requiredFieldList OkForDCOrbitCorrectionV]==-1 } {
        return -code error "SetBPMBadBOValues: Internal error: field list is invalid!"
    }
    global OkForDCOrbitCorrectionH OkForDCOrbitCorrectionV
    global NonexistentH NonexistentV
    global sectorSelection deviceTypeSelection sectorSelectionSaved deviceTypeSelectionSaved
    if [regexp {booster} $system] {
        set newDeviceName $sectorSelection$deviceTypeSelection 
    } else {
        set newDeviceName S$sectorSelection$deviceTypeSelection 
    }

    set FieldsHaveChanged 0
    foreach field $requiredFieldList {
        global $field
        if [string compare $field Comments]!=0 {
            if [string compare [set $field\(0\)] [set $field\($newDeviceName\)]]!=0 {
                incr FieldsHaveChanged
            }
        }
    }

    set nDevices [llength $deviceList]
    foreach deviceName $deviceList {
        foreach HV {H V} xy {x y} {
            if [set Nonexistent${HV}\($deviceName\)] continue
            set state [set OkForDCOrbitCorrection${HV}\($deviceName\)]
            if $state {
                lappend cavputOptList $deviceName:ms:$xy=0
            } else {
                lappend cavputOptList $deviceName:ms:$xy=1
                setStatus "$deviceName $xy is bad"
            }
        }
    }
    setStatus "Setting BadBO values..."
    exec cavput -list=[join $cavputOptList ,] -list=:BadBO -pendIO=30
    setStatus "Done."
}

proc GetConfigMTime {args} {
    set filename [file tail [file readlink config.sdds]]
    regsub {config-} $filename "" dateString
    set year [string range $dateString 0 3]
    set month [string range $dateString 9 10]
    set day [string range $dateString 11 12]
    set hour [string range $dateString 14 15]
    set min [string range $dateString 16 17]
    set sec [string range $dateString 18 19]
    return [clock scan ${hour}:${min}:${sec} -base [clock scan ${year}-${month}-${day}]]
}

set apsScriptCommand $argv0
set args $argv
set system bpms
if {[APSStrictParseArguments {system}] || ![string length $system]} {
    APSAlertBox [APSUniqueName .] -errorMessage "Usage: ManageSRDeviceStatus -system {bpms|hcorrectors|vcorrectors}"
}
set combinedFilesOnly 1
switch $system {
    bpms {
        set dataDir /home/helios/oagData/bts/BPMStatus
        set combinedFilesOnly 0
    }
    correctors {
        set dataDir /home/helios/oagData/bts/CorrectorStatus
    }
    default {
        APSAlertBox [APSUniqueName .] -errorMessage "Usage: ManageSRDeviceStatus -system {bpms|hcorrectors|vcorrectors}"
    }
}

if {![file exists $dataDir] || ![file isdirectory $dataDir]} {
    APSAlertBox [APSUniqueName .] -errorMessage "$dataDir is nonexistent or not a directory"
}
cd $dataDir
set commandList ""

set deviceList [exec sdds2stream -col=Prefix  prefixes.sdds]
set labelList $deviceList
foreach device $deviceList {
    lappend commandList "ProcessSelectionChange"
}

set changedDeviceList ""
set assumeAllChanged 0
set ConfigMTime0 [GetConfigMTime]
set theTime [clock seconds]
set loadingDate [clock format $theTime -format "%Y %j"]


#following will generate comments.sdds if it does not exist for that device
if [string match "bpm*" $system] {
    set deviceList [exec sdds2stream -col=Prefix prefixes.sdds]
    set deviceList1 [exec sdds2stream config.sdds -col=DeviceName]
    foreach device $deviceList1 {
        if ![file exists $device/comments.sdds] {
          #  puts "Making comment file for $device"
          #  setStatus "Making comment file for $device"
          #  ShowHistoryBPMs -commentsOnly 1 -deviceName $device -display 0
        }
    }
}

PreBuildApplication -system $system
ReadApplicationConfig -system $system
BuildApplication -system $system
ReadConfiguration
setStatus "Done."


# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:
