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

set CVSRevisionAuthor "\$Revision: 1.5 $ \$Author: soliday $"

APSApplication . -name SnapshotSequenceComparison -version $CVSRevisionAuthor \
      -overview "This application takes as input a snapshot sequence file created by SnapshotSequenceRecorder.  It then sequentially compares each snapshot in the sequence to the present state of the machine."

set statusText ""
proc setStatusText { text } {
    global statusText
    set statusText $text
    update
}

set workingDir ""
set sequenceFile ""
set sequenceDescription None
set sequenceFileOpen 0
set stepDescription None
set preStepPause 0
set postStepPause 0
set sequenceStep ?
set actSeqNumOfSnapshots 0
set refsnapName ""
set actSeqFileName ""
set refSnapCounter 0
set snapshotList ""

setStatusText "Enter filter directory and search for reference sequence file."

APSScrolledStatus .status -parent .userFrame -textVariable statusText -width 80 \
      -height 10


#
# This procedure is run just after APSFileSelectDialog is run.
# It completes the process of picking the snapshot sequence.
# It reads some data from the snapshot sequence, just as the
# sequence description and the list of snapshots.
#

proc PickSeqFile {} {
    global actSeqFileName workingDir statusText snapshotList apsFileSelect actSeqNumOfSnapshots
    
    set workingDir [file dirname $apsFileSelect(filter)] 

    set actSeqFileName [file tail $apsFileSelect(select)]

    set actSeqDescription [lindex [APSGetSDDSParameter -page 0 -parameter SequenceDescription \
                                    -fileName $apsFileSelect(select)] 0]
    set snapshotList [APSGetSDDSColumn -page 0 -column Snapshot \
                        -fileName $apsFileSelect(select)]
    set actSeqNumOfSnapshots [llength $snapshotList]

    setStatusText ""
    setStatusText "Sequence description:     ---- $actSeqDescription ----"
    setStatusText "Number of steps: $actSeqNumOfSnapshots"
    setStatusText "Look at the name and the description of the first snapshot in the Reference Sequence File."
    
    global typeList
    set typeList [APSGetSDDSColumn -page 0 -column Type \
                    -fileName $apsFileSelect(select)]
    RefSnapSetup 
}

# This procedure sets up the reference snapshot data for the first
# snapshot.  It reads the snapshot description and sets a few global
# variables.

proc RefSnapSetup {} {
    global snapshotList refSnapName refSnapDescr workingDir refSnapCounter
    global setStatusText stepDescription typeList
    
    set index 0
    setStatusText "Reading reference snapshot data."
    while {$index<[llength $typeList]} {
        set type [lindex $typeList $index]
        if [string compare $type snapshot]==0 break
        incr index
    }
    if $index==[llength $typeList] {
        setStatus "No snapshots in file."
    } else {
        set refSnapName [lindex $snapshotList $index]
        
        set refSnapDescr [lindex [APSGetSDDSParameter -page 0 \
                                -parameter SnapshotDescription -fileName $workingDir/$refSnapName] 0]
        set stepDescription $refSnapDescr
        set refSnapCounter [expr $index+1]
        update
    }
}

# This procedure closes a snapshot sequence file, if one is open.
# The file is opened with openSequence.

proc closeSequence {} {
     global sequenceFileOpen seqFID sequenceStep refSnapDescr

     if !$sequenceFileOpen {
         setStatusText "No sequence is open."
         return
     }
     close $seqFID
     set sequenceFileOpen 0
     set sequenceStep ?
     allowSequenceEntry 1
     setStatusText "Sequence closed."
     setOpenCloseButtons 1
     setActionButtons 0

     ResetSnapshot
}

# This procedure writes the description of a reference snapshot
# to a scrolled text area.

proc refSnapDescrAction {state} {
    global refSnapDescr 
    
    update
    if $state {
        .userFrame.descr.frame.snapDescr.text insert end "$refSnapDescr"
    } else {
        .userFrame.descr.frame.snapDescr.text delete 1.0 end        
    }
}

# This procedure opens a snapshot sequence.  A snapshot sequence must be
# opened in order to do any comparisons.  The process of comparing to
# an existing sequence creates a new sequence of the states at the
# time of comparison.

proc openSequence {} {
    global sequenceFileOpen workingDir sequenceFile seqFID sequenceStep sequenceDescription

    if $sequenceFileOpen {
        setStatusText "Close the existing sequence before starting a new one."
        return
    }

    set workingDir [string trim $workingDir]
    set sequenceFile [string trim $sequenceFile]
    if [string length $sequenceFile]==0 {
        set statusText "No filename given"
        return
    }
    if [string length $workingDir]==0 {
        set file $sequenceFile
    } else {
        set file $workingDir/$sequenceFile
    }

    setStatusText "Opening sequence..."
    set choice Overwrite
    if [file exists $file] {
        catch {APSMultipleChoice [APSUniqueName .] \
                 -question "File $sequenceFile exists in $workingDir. What do you want to do?" \
                 -labelList {Append Overwrite Cancel} \
                 -returnList  {Append Overwrite Cancel}} choice
        }
    switch $choice {
        Overwrite {
            catch {exec cp $file $file.bck}
            set seqFID [open $file w]
            set append 0
        }
        Append {
            catch {exec cp $file $file.bck}
            set seqFID [open $file a]
            set append 1
        }
        Cancel {
            setStatusText "New sequence aborted."
            return
        }
        default {
            setStatusText "Unknown return value: $choice"
            return
        }
    }
    set sequenceFileOpen 1
    allowSequenceEntry 0
    setOpenCloseButtons 0
    if !$append {
        puts $seqFID "SDDS1"
        puts $seqFID "&parameter name=SequenceDescription type=string fixed_value=\"$sequenceDescription\" &end"
        puts $seqFID "&column name=Step type=long &end"
        puts $seqFID "&column name=Type type=string &end"
        puts $seqFID "&column name=Description type=string &end"
        puts $seqFID "&column name=Snapshot type=string &end"
        puts $seqFID "&column name=System type=string &end"
        puts $seqFID "&column name=PreStepPause type=double &end"
        puts $seqFID "&column name=PostStepPause type=double &end"
        puts $seqFID "&column name=Time type=double units=s &end"
        puts $seqFID "&data mode=ascii no_row_counts=1 &end"
        flush $seqFID
    } else {
        set sequenceStep ?
        set expectedColumnList [list Step Type Description Snapshot System PreStepPause PostStepPause Time]
        set columnList [APSGetSDDSNames -fileName $file -class column]
        set sequenceDescription [APSGetSDDSParameter -fileName $file -parameter SequenceDescription]
        if [llength $expectedColumnList]!=[llength $columnList] {
            setStatusText "Mismatch of existing file with expected columns:"
            setStatusText "Existing: $columnList"
            setStatusText "Expected: $expectedColumnList"
            close $seqFID
            set sequenceFileOpen 0
            allowSequenceEntry 1
            setOpenCloseButtons 1
            return
        }
        foreach elem $columnList {
            if [lsearch -exact $columnList $elem]!=[lsearch -exact $expectedColumnList $elem] {
                setStatusText "Mismatch of existing file with expected columns:"
                setStatusText "Existing: $columnList"
                setStatusText "Expected: $expectedColumnList"
                close $seqFID
                set sequenceFileOpen 0
                allowSequenceEntry 1
                setOpenCloseButtons 1
                return
            }
        }
        set stepList [APSGetSDDSColumn -fileName $file -column Step -page 1]
    }
    set sequenceStep 1
    setStatusText "Sequence being recorded in $file"
    update

    setActionButtons 1
}

# Set the state of the "Add & Comp snapshot" button.

proc setActionButtons {state} {
    if $state {
        APSEnableButton .userFrame.butFrame.frame.addSnap.button
    } else {
        APSDisableButton .userFrame.butFrame.frame.addSnap.button
    }
}

# Set the state of the "Open sequence" and "Close sequence" buttons.

proc setOpenCloseButtons {state} {
    if $state {
        APSDisableButton .userFrame.butFrame.frame.close.button
        APSEnableButton .userFrame.butFrame.frame.open.button
    } else {
        APSEnableButton .userFrame.butFrame.frame.close.button
        APSDisableButton .userFrame.butFrame.frame.open.button
    } 
}

# Adds data to an already-open snapshot sequence file.

proc AddDataToSeqFile {args} {
    global seqFID sequenceStep stepDescription sequenceFileOpen 
    global preStepPause postStepPause
    APSStrictParseArguments {Type Snapshot}
    if [string length $Snapshot] {
        set Snapshot [file tail $Snapshot]
    }
    set apsSCRSystem "Custom"
    puts -nonewline $seqFID "$sequenceStep $Type \"$stepDescription\" \"$Snapshot\" "
    puts $seqFID " \"$apsSCRSystem\" $preStepPause $postStepPause [exec timeconvert -breakdown=now]"
    flush $seqFID
    incr sequenceStep
}


# Enable/disable entry boxes for sequence filename and sequence description.
# These data are input prior to opening the sequence and can't be changed
# until the sequence is closed.

proc allowSequenceEntry {state} {
    if $state {
        .userFrame.newSeqFile.entry configure -state normal -foreground black -takefocus 1
        .userFrame.seqDescrip.entry configure -state normal -foreground black -takefocus 1
    } else {
        .userFrame.newSeqFile.entry configure -state disabled -foreground grey -takefocus 0
        .userFrame.seqDescrip.entry configure -state disabled -foreground grey -takefocus 0
    }
}

# Resets snapshot variables for starting over with the same
# reference sequence.

proc ResetSnapshot {} {
     global refSnapName 
  
     set refSnapName ""
     RefSnapSetup
     update
}

# Takes another snapshot in the comparison sequence and saves it.
# Also does a comparison of the snapshot to the reference snapshot
# for the current step.

proc addSnapshot {} {
    global sequenceFileOpen sequenceStep workingDir sequenceFile refSnapDescr
    global customRequestFile stepDescription refSnapName refSnapCounter
    global actSeqFileName actSeqNumOfSnapshots snapshotList
    global sortChoice absoluteTolerance percentTolerance showSimilarities

    if !$sequenceFileOpen {
        setStatusText "Can't add snapshot---sequence not open."
        return
    }

    if [string length $workingDir] {
        set snapshot $workingDir/$sequenceFile-[exec date +%Y-%j-%m%d-%H%M%S]
    } else {
        set snapshot $sequenceFile-[exec date +%Y-%j-%m%d-%H%M%S]
    }

    set requestFile $workingDir/$refSnapName

    if ![file exists $requestFile] {
        setStatusText "Request file $requestFile not found."
        return
    }
    setActionButtons 0
    setStatusText "Taking snapshot..."
    set logFile /tmp/[APSTmpString]
    catch {exec timeconvert -now} timeList 
    set time0 [format %.0lf [lindex $timeList 8]]
    set description [APSMakeSafeQualifierString $stepDescription]
    if [catch \
          {exec burtrb -f $requestFile -sdds -l $logFile \
             | sddsconvert -binary -pipe \
             | sddsxref -pipe $requestFile -take=* -nowarning \
             | sddsprocess -pipe=in $snapshot \
             "-define=parameter,Time,$time0,units=s,type=long" \
             "-print=parameter,SnapshotDescription,$description" \
             "-print=parameter,RequestFile,$requestFile" \
             "-print=parameter,SnapshotFilename,${snapshot}"} result] {
        setStatusText "Error taking snapshot: $result"
        setActionButtons 1
        if ![file exists $logFile] return
    } 

    if {[file exists $logFile] && ![file size $logFile]} {
        exec rm $logFile
    }
    if [file exists $logFile] {
        setStatusText "Snapshot taken, but errors found."
        set tmpWidget [APSUniqueName .] 
        APSFileDisplayWindow $tmpWidget -fileName $logFile \
          -comment "Error log from step $sequenceStep in sequence $sequenceFile" \
          -contextHelp \
          "This widget displays an error log from an attempt to create a snapshot.  Press Continue or Cancel to proceed." \
          -width 120 
        pack forget $tmpWidget.buttonRow.close
        global snapshotErrorCancel
        APSDialogBoxAddButton .continue -parent $tmpWidget -text Continue \
          -contextHelp "Continue addition of snapshot to the sequence, in spite of errors." \
          -command "set snapshotErrorCancel 0"
        APSDialogBoxAddButton .cancel -parent $tmpWidget -text Cancel \
          -contextHelp "Cancel addition of snapshot to the sequence." \
          -command "set snapshotErrorCancel 1"
        tkwait variable snapshotErrorCancel
        catch {exec rm $logFile}
        destroy $tmpWidget
        if $snapshotErrorCancel {
            setStatusText "Step aborted."
            setActionButtons 1
            return
        }
    }
    AddDataToSeqFile -Type snapshot -Snapshot $snapshot
    setActionButtons 1
    setStatusText "Snapshot added."


    setStatusText "Working..."
    if [catch {APSCompareMachine -snapshot1 $requestFile \
                 -sortChoice $sortChoice -statusCallback setStatusText \
                 -snapshot2 $snapshot -graphics 0 \
                 -absoluteTolerance $absoluteTolerance \
                 -percentTolerance $percentTolerance \
                 -showSimilarities $showSimilarities } result] {
        APSAlertBox [APSUniqueName .] -errorMessage "Error doing compare: $result"
        return
    }
    set sdiffFile [lindex $result 0]
    set enumDiff [lindex $result 1]
    set printFile1 [lindex $result 2]
    set printFile2 [lindex $result 3]

    catch {exec sdds2stream $sdiffFile -rows} rows_1
    catch {exec sdds2stream $enumDiff -rows} rows_2

    set arows [string trimright [APSStringTrimRight ${rows_1} rows]]
    set brows [string trimright [APSStringTrimRight ${rows_2} rows]]

    if $showSimilarities {
          set compareType similarities
    } else {
          set compareType differences
    }


    if {${arows} == 0 && ${brows} == 0} {
        setStatusText ""
        setStatusText "No differences"
    } elseif {${arows} != 0 && ${brows} == 0} {
           APSFileDisplayWindow [APSUniqueName .] \
              -fileName $printFile1 -deleteOnClose 1 -width 120 -height 50 \
              -comment "Numerical-value snapshot $compareType"
    } elseif {${arows} == 0 && ${brows} != 0} {
           APSFileDisplayWindow [APSUniqueName .] \
              -fileName $printFile2 -deleteOnClose 1 -width 120 -height 50 \
              -comment "Enumerated-value snapshot $compareType"
    } else {
           APSFileDisplayWindow [APSUniqueName .] \
              -fileName $printFile1 -deleteOnClose 1 -width 120 -height 50 \
              -comment "Numerical-value snapshot $compareType"
           APSFileDisplayWindow [APSUniqueName .] \
              -fileName $printFile2 -deleteOnClose 1 -width 120 -height 50 \
              -comment "Enumerated-value snapshot $compareType"
    }    

    setStatusText "Comparison done." 

    global typeList
    while 1 {
        incr refSnapCounter
        if {$refSnapCounter>$actSeqNumOfSnapshots} {
            setStatusText ""
            setStatusText "**End of reference sequence.**"
            closeSequence
            return
        }
        set type [lindex $typeList [expr $refSnapCounter - 1]]
        if [string compare $type snapshot]==0 {
            set refSnapName [lindex $snapshotList [expr $refSnapCounter - 1]]
            set refSnapDescr [join [APSGetSDDSParameter -page 0 \
                                      -parameter SnapshotDescription -fileName $workingDir/$refSnapName]]
            setStatusText "Will compare with $refSnapName"
            refSnapDescrAction 0
            refSnapDescrAction 1
            set stepDescription $refSnapDescr
            break
        } else {
            setStatusText "Skipping step of type $type"
            update
        }
    }

    update
}

APSFileSelectDialog .refSeq1 -parent .userFrame

PickSeqFile

APSFrame .ref -parent .userFrame -label "Reference information:" -relief flat

APSLabeledOutput .workDir -parent .userFrame.ref.frame -label "Working directory: " \
      -textVariable workingDir -width 60 -contextHelp \
      "Displays the directory you want to work in."

APSLabeledOutput .actseq -parent .userFrame.ref.frame -textVariable actSeqFileName \
      -label "Reference Sequence File" -width 60 -contextHelp \
      "Displays active reference sequence file picked from the working directory."

APSLabeledOutput .actsnap -parent .userFrame.ref.frame -textVariable refSnapName \
      -label "Reference Snapshot" -width 60 -contextHelp \
      "Displays next reference snapshot from the active reference sequence file\
ready for the comparison."

APSLabeledOutput .counter -parent .userFrame.ref.frame -textVariable refSnapCounter \
      -label "Reference Snapshot Step Counter" -width 60 -contextHelp \
      "Displays the current step in the refernce sequence."

APSFrame .descr -parent .userFrame -label "Reference Snapshot Description:" -relief flat 
APSScrolledText .snapDescr -parent .userFrame.descr.frame -name "Reference Snapshot Description" \
       -width 60 -height 3 

APSLabeledEntry .newSeqFile -parent .userFrame -label "New sequence file: " \
      -textVariable sequenceFile -width 60 -contextHelp \
      "Enter the name of the new sequence file you want to create."
APSLabeledEntry .seqDescrip -parent .userFrame -label "Sequence description: " \
       -textVariable sequenceDescription -width 60 \
       -contextHelp "Enter a description for the sequence you are creating."
APSLabeledEntry .stepDescrip -parent .userFrame -label "Step description: " \
       -textVariable stepDescription -width 60 \
       -contextHelp "Enter a description of the present sequence step."
APSLabeledEntry .preStepPause -parent .userFrame -label "Pre-step pause (seconds): " \
       -textVariable preStepPause -width 60 \
       -contextHelp "Enter the number of seconds to pause prior to the present step."
APSLabeledEntry .postStepPause -parent .userFrame -label "Post-step pause (seconds): " \
       -textVariable postStepPause -width 60 \
       -contextHelp "Enter the number of seconds to pause after the present step."
APSLabeledOutput .seqStep -parent .userFrame -label "Current sequence step: " \
       -textVariable sequenceStep -width 60 \
       -contextHelp "Displays the present step in the sequence."

update

APSFrame .butFrame -parent .userFrame -packOption "-side top"
APSButton .addSnap -parent .userFrame.butFrame.frame -text "Add & Comp snapshot" \
  -command addSnapshot  \
  -contextHelp "Adds a snapshot in the sequence and compares it with the reference snapshot."
APSButton .open -parent .userFrame.butFrame.frame -text "Open sequence" \
  -command openSequence -contextHelp \
  "Opens a new snapshot sequence or continues an existing one."
APSButton .close -parent .userFrame.butFrame.frame -text "Close sequence" \
  -command closeSequence -contextHelp \
  "Closes out any existing sequence you are working on."

APSFrame .tol -parent .userFrame -label "Comparison entries" -relief flat \
  -contextHelp "These are entry fields for setting tolerances other than the preordained ones. \
   To use, put a nonzero value in one of the boxes.  If you put values in both, the absolute \
   tolerance is used." -packOption "-side top -fill x"

set w .userFrame
set percentTolerance 0
set absoluteTolerance 0
set showSimilarities 0

APSLabeledEntry .percentTol -parent $w.tol.frame -width 10 -packOption "-expand 1 -side left" \
  -label "Percent tolerance: " -textVariable percentTolerance \
  -contextHelp "Enter a percentage for comparisons of numerical values.  \
   Only new values that have changed by the given percent of the reference \
   value will be displayed.  Ignored if 0, or if a nonzero absolute tolerance is given."
APSLabeledEntry .absoluteTol -parent $w.tol.frame -width 10 -packOption "-expand 1 -side left" \
  -label "Absolute tolerance: " -textVariable absoluteTolerance \
  -contextHelp "Enter a tolerance for comparisons of numerical values.  Only new values that \
   have changed by the given amount will be displayed.  Ignored if 0."
APSRadioButtonFrame .compareType -parent $w.tol.frame -packOption "-expand 1 -side left" \
  -label "Show " -buttonList "Differences Similarities" -valueList "0 1" \
  -variable showSimilarities -orientation horizontal \
  -contextHelp "Choose whether to view differences or similarities between snapshots."

APSFrame .ops -parent $w -label "" -relief flat
set sortChoice none
APSRadioButtonFrame .sort -parent $w.ops.frame -label "  Sort by: " \
      -buttonList {none fracError value error name} \
      -valueList {none byFracError byValue byError byName} \
      -orientation horizontal -packOption "-side left" \
      -variable sortChoice \
      -contextHelp "Allows choosing the order in which data will be listed in the printouts.  Sorting by fractional error will bring the items with the largest difference relative to their tolerance to the top. Sorting by error will bring the items with the largest difference to the top.  Sorting by value will bring the item with the largest value to the top.  Sorting by name orders items alphabetically."

refSnapDescrAction 1
setOpenCloseButtons 1
setActionButtons 0
