# ========================================================================================================
#
# John Pritchard
#
# Developed from esotk_fringe_obj_mask.py by Mark Neeser
#
# Contains the base class BaseDataPlotterManager and necessary customisations to
# PipelineInteractiveApp to have interactive GUIs with necessary controls that
# allow to navigate the FITS file extensions more easily.
# All interactive python actors for the esotk fringing workflow should use these
# common classes and methods.
#
# Author: Artur Szostak <aszostak@partner.eso.org>
# ------------------------------------------------------------------------------
# Major! adaptations for GIRAFFE
#
# ChangeLog:
#   2017-03: removed Extension slider etc as GIRAFFE products do not have extensions...

import os
import sys
import re
import reflex_interactive_app
import reflex_interactive_gui

def get_attrs(klass):
  return [k for k in list(klass.__dict__.keys())
            if not k.startswith('__')
            and not k.endswith('__')]

try:
    # Import non-standard but needed modules.
    import numpy
    try :
        from astropy.io import fits as pyfits
    except:
        import pyfits
    import reflex
    import pipeline_product
    import pipeline_display
    import matplotlib
    import os.path, glob
    from astropy.table import Table
    import wx
    from wx import xrc
    import_success = True


    def MAD(x):
        """
        Median absolute deviation function; used to scale the images.
        """
        x = numpy.array(x)
        return numpy.median(numpy.abs(x - numpy.median(x)))


    class BaseDataPlotterManager(object):
        """
        Common base class that must be inherited by the different interactive
        fringing GUIs to correctly setup the customised GUI with FITS
        navigation controls.
        """

        def __init__(self):
            self.xrc_file = os.path.join(os.path.dirname(__file__),
                                         'esotk_giraf_sci_gui.xrc')
            self.n_ext = 0
            self.ext_id = 0
            self.science_frames = []
            # The following can only be filled in after initialisation.
            self.objectByName = None
            self.objectBySNR = None
            self.fileChoice = None
            self.extSlider = None
            self.figure = None
            self.CRCM = None

            # The following are static and could be filled in now, but it's easier to follow the examples and do it below...
            self.skyApplyToAllOrOne = None
            self.skyApplicationMethod = None
            self.skyNumSkyFibs = None
            self.skyNumSkyFibsHasBeenSet = False
            self.skyCompMethod = None
            self.skyFibManualSelection = None
            self.skyRejectHighestSNRfibCheckBox = None

            self.NoCRC_displaySignalCheckBox = None
            self.NoCRC_displaySignalMinusSkyCheckBox = None
            self.NoCRC_displaySkyCheckBox = None
            self.NoCRC_displayErrCheckBox = None
            self.AS_displaySignalCheckBox = None
            self.AS_displaySignalMinusSkyCheckBox = None
            self.AS_displaySkyCheckBox = None
            self.AS_displayErrCheckBox = None
            self.PC_displaySignalCheckBox = None
            self.PC_displaySignalMinusSkyCheckBox = None
            self.PC_displaySkyCheckBox = None
            self.PC_displayErrCheckBox = None
            self.MED_displaySignalCheckBox = None
            self.MED_displaySignalMinusSkyCheckBox = None
            self.MED_displaySkyCheckBox = None
            self.MED_displayErrCheckBox = None

        def setupExtensionValues(self, products):
            ## Not needed by Giraffe
            pass

        def _setupExtensionValues(self, products):
            """
            This method should be called in the readFitsData() method to update
            the maximum extensions value and the current extension ID. The
            needed values are taken from the PipelineProduct object given by
            'products'.
            """
            self.n_ext = len(products.all_hdu)
            if self.n_ext > 1:
                self.ext_id = 1
            else:
                self.ext_id = 0

        def setupFigure(self, figure):
            self.figure = figure
            pass

        def setupExtensionLabel(self, figure):
            ## Not needed by Giraffe
            pass

        def _setupExtensionLabel(self, figure):
            """
            This method must be called in the addSubplots() method to setup the
            extension label in the Matplotlib figure.
            """
            self.extLabel = figure.text(0.01, 0.97, "Extension 0 of 0")
            # Keep the figure to redraw plots in the event handlers.
            self.figure = figure

        def updateExtensionLabel(self):
            ## Not needed by Giraffe
            pass

        def _updateExtensionLabel(self):
            """
            This function should be called in the plotProductsGraphics() method
            to update the extension label in the Matplotlib figure.
            """
            text = "Extension {0} of {1}".format(self.ext_id, self.n_ext - 1)
            self.extLabel.set_text(text)

        def computeRobustMedMad(self, image):
            """
            Helper method to compute the median and MAD values returned as a
            tuple for the given image.
            """
            med = numpy.median(image)
            mad = MAD(image)
            new_image = image[numpy.abs(image-med) < 3*1.48*mad]
            new_med = numpy.median(new_image)
            new_mad = MAD(new_image)
            return new_med, new_mad

        def onClickNextFile(self, event):
            """
            This event handler is called whenever the 'next' button is clicked
            for the file selection. Need to update the selected file and then
            update the plots.
            """
            val = self.fileChoice.GetSelection() + 1
            if val >= self.fileChoice.GetCount():
                return
            self.fileChoice.SetSelection(val)
            self.onFileChange(None)

        def onClickPreviousFile(self, event):
            """
            This event handler is called whenever the 'previous' button is
            clicked for the file selection. Need to update the file choice and
            plots, like for onClickNextFile.
            """
            val = self.fileChoice.GetSelection() - 1
            if val < 0:
                return
            self.fileChoice.SetSelection(val)
            self.onFileChange(None)

        def loadProducts(self, index):
            """
            Function to load the products based on the given file selection
            indicated by 'index'. i.e. the index'th frame from the array
            self.science_frames. This method can be overloaded if more than one
            image needs to be loaded.
            """
            frame = self.science_frames[index]
            self.science = pipeline_product.PipelineProduct(frame)
            self.CRCM=self.frameCRCMLabels[frame.name]

        def onFileChange(self, event):
            """
            This event handler is called whenever the file choice changes from
            the drop down list. We need to update the current loaded file and
            redraw the plots.
            """
            index = self.fileChoice.GetSelection()
            if index >= 0 and index < len(self.science_frames):
                self.loadProducts(index)
                self._computeSky()
                self.plotProductsGraphics()
                self.updateFrameStrings()
                self.figure.canvas.draw()

        def setObjectByNameAndBySNRFromFIB_ID(self):
            self.objectBySNR.SetSelection(numpy.where(self.sci_rbnspectra_extr.objectSkyBySNR_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0])
            self.objectByName.SetSelection(numpy.where(self.sci_rbnspectra_extr.objectSkyByName_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0])

        def updateFrameStrings(self):
            self.objectByName.Clear()
            for i in self.sci_rbnspectra_extr.objectSkyByName_ind :
                OBJECT = self.sci_rbnspectra_extr.objectList[i]
                FPS = self.sci_rbnspectra_extr.FPSList[i]
                SSN = self.sci_rbnspectra_extr.SSNList[i]
                OBJECT_SNR = self.sci_rbnspectra_extr.SNRarr[self.CRCM][i]
                str=" %s, S/N = %5.2f [FPS/SSN=%d/%d, col=%d]" %(OBJECT.strip(),OBJECT_SNR,FPS,SSN,i+1)
                self.objectByName.Append(str)
            self.objectBySNR.Clear()
            for i in self.sci_rbnspectra_extr.objectSkyBySNR_ind :
                OBJECT = self.sci_rbnspectra_extr.objectList[i]
                FPS = self.sci_rbnspectra_extr.FPSList[i]
                SSN = self.sci_rbnspectra_extr.SSNList[i]
                OBJECT_SNR = self.sci_rbnspectra_extr.SNRarr[self.CRCM][i]
                str=" %s, S/N = %5.2f [FPS/SSN=%d/%d, col=%d]" %(OBJECT.strip(),OBJECT_SNR,FPS,SSN,i+1)
                self.objectBySNR.Append(str)
            self.setObjectByNameAndBySNRFromFIB_ID()
            


        def onObjectByNameChange(self, event):
            """
            This event handler is called whenever the objectByName choice changes from
            the drop down list. We need to update the currently displayed spectrum
            """
            index = self.objectByName.GetSelection()
            if index >= 0 and index < len(self.sci_rbnspectra_extr.objectSkyByName_ind):
                self.fib_id=self.sci_rbnspectra_extr.objectSkyByName_ind[index]+1
                self.sci_rbnspectra_extr.selectObject(self.fib_id)
                #self.objectBySNR.SetSelection(range(len(self.sci_rbnspectra_extr.objectSkyBySNR_ind))[numpy.where(self.sci_rbnspectra_extr.objectSkyBySNR_ind == self.sci_rbnspectra_extr.objectSkyByName_ind[index])[0][0]])
                self.objectBySNR.SetSelection(numpy.where(self.sci_rbnspectra_extr.objectSkyBySNR_ind == self.fib_id-1)[0][0])
                self.plotProductsGraphics()
                self.figure.canvas.draw()

        def onObjectBySNRChange(self, event):
            """
            This event handler is called whenever the objectBySNR choice changes from
            the drop down list. We need to update the currently displayed spectrum
            """
            index = self.objectBySNR.GetSelection()
            if index >= 0 and index < len(self.sci_rbnspectra_extr.objectSkyBySNR_ind):
                self.fib_id=self.sci_rbnspectra_extr.objectSkyBySNR_ind[index]+1
                self.sci_rbnspectra_extr.selectObject(self.fib_id)
                self.objectByName.SetSelection(numpy.where(self.sci_rbnspectra_extr.objectSkyByName_ind == self.fib_id-1)[0][0])
                self.plotProductsGraphics()
                self.figure.canvas.draw()

        def onClickSetCuts(self, event):
            """
            This event handler is called whenever the 'previous' button is
            clicked for the file selection. Need to update the file choice and
            plots, like for onClickNextFile.
            """
            self.sci_rbnspectra_extr.init_spec_subplot(self.subplot_sci_rbnspectra_extr, CRCM=self.fileChoice.GetString(self.fileChoice.GetSelection()))
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        ## Sky Subtraction...
        def _computeSky(self):
            self.computeSky(
                self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()),
                self.skyApplicationMethod.GetString(self.skyApplicationMethod.GetSelection()),
                self.skyCompMethod.GetString(self.skyCompMethod.GetSelection()),
                self.skyNumSkyFibs.GetSelection()+1
                )

        def onSkyApplyToAllOrOne(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            index = self.skyApplyToAllOrOne.GetSelection()
            if index >= 0 :
                self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.applyTo')].paramChangeCtrl.SetValue(self.skyApplyToAllOrOne.GetString(index))
                self.enableCheckBoxes()
                self._computeSky()
                self.plotProductsGraphics()
                self.figure.canvas.draw()
        
        def enableCheckBoxes(self) :
                self.NoCRC_displaySignalCheckBox.Enable("NONE" in list(self.sf.keys()))
                self.NoCRC_displaySkyCheckBox.Enable("NONE" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.NoCRC_displaySignalMinusSkyCheckBox.Enable("NONE" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.AS_displaySignalCheckBox.Enable("ASTROSCRAPPY" in list(self.sf.keys()))
                self.AS_displaySkyCheckBox.Enable("ASTROSCRAPPY" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.AS_displaySignalMinusSkyCheckBox.Enable("ASTROSCRAPPY" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.PC_displaySignalCheckBox.Enable("PYCOSMIC" in list(self.sf.keys()))
                self.PC_displaySkyCheckBox.Enable("PYCOSMIC" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.PC_displaySignalMinusSkyCheckBox.Enable("PYCOSMIC" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.MED_displaySignalCheckBox.Enable("MEDIAN" in list(self.sf.keys()))
                self.MED_displaySkyCheckBox.Enable("MEDIAN" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")
                self.MED_displaySignalMinusSkyCheckBox.Enable("MEDIAN" in list(self.sf.keys()) and self.skyApplyToAllOrOne.GetString(self.skyApplyToAllOrOne.GetSelection()) != "None")                

        def onSkyApplicationMethod (self, event):
            """
            This event handler is called whenever the Application Method
            changes from the drop down list. We need to update the method...
            """
            index = self.skyApplicationMethod.GetSelection()
            if index >= 0 :
                self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.method')].paramChangeCtrl.SetValue(self.skyApplicationMethod.GetString(index))
                self._computeSky()
                self.plotProductsGraphics()
                self.figure.canvas.draw()

        def onSkyNumSkyFibs (self, event):
            """
            This event handler is called whenever the Number of fibres
            changes from the drop down list. We need to update the method...
            """
            index = self.skyNumSkyFibs.GetSelection()
            self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.numFibs')].paramChangeCtrl.SetValue(index+1)
            self.skyNumSkyFibsHasBeenSet = True
            if index+1 == len(self.sci_rbnspectra_extr.skyListSNR[self.sci_rbnspectra_extr.skyFibIsSelected][self.sci_rbnspectra_extr.skyRejected]) :
                self.skyNumSkyFibsHasBeenSet = False
            if index >= 0 :
                self._computeSky()
                self.plotProductsGraphics()
                self.figure.canvas.draw()

        def onSkyCompMethod(self, event):
            """
            This event handler is called whenever the Computational Method selected
            changes from the drop down list. We need to update the method...
            """
            index = self.skyCompMethod.GetSelection()
            if index >= 0 :
                self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.statistic')].paramChangeCtrl.SetValue(self.skyCompMethod.GetString(index))
                self._computeSky()
                self.plotProductsGraphics()
                self.figure.canvas.draw()

        def setSkyFibIsSelected(self) :
            try:
                sfis=numpy.zeros(len(self.sci_rbnspectra_extr.skyBySNR_ind), dtype=bool)
                sfis[list(self.skyFibManualSelection.GetCheckedItems())]=True
                self.sci_rbnspectra_extr.skyFibIsSelected=sfis
            except:
                # GetCheckedItems() used above not supported in wxPython-2.8
                sfis=[]
                for i in range(len(self.sci_rbnspectra_extr.skyBySNR_ind)) :
                    sfis.append(self.skyFibManualSelection.IsChecked(i))
                self.sci_rbnspectra_extr.skyFibIsSelected=numpy.array(sfis)
            self.setSkyRejected()

        def setSkyFibIsSelectedFromParam(self) :
            skyObjs=numpy.array([str(x).strip() for x in self.sci_rbnspectra_extr.objectList[self.sci_rbnspectra_extr.skyBySNR_ind]])
            sfis=numpy.ones(len(skyObjs),dtype=bool)
            for o in numpy.array(self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list')].paramChangeCtrl.GetValue().split(","), dtype='|S30') :
                sfis*=numpy.logical_not(skyObjs == o.decode('utf-8'))
            try:
                if numpy.any(sfis) :
                    self.skyFibManualSelection.SetCheckedItems(numpy.where(sfis == True)[0])
            except:
                # SetCheckedItems() used above not supported in wxPython-2.8
                for (i,tf) in enumerate(sfis) :
                    self.skyFibManualSelection.Check(i,tf)
            self.sci_rbnspectra_extr.skyFibIsSelected=sfis
            self.setSkyRejected()

        def onSkyRejectHighestSNRfibCheckBox(self, event):
            self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.reject')].paramChangeCtrl.SetValue(self.skyRejectHighestSNRfibCheckBox.GetValue())
            self.setSkyRejected()
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def setSkyRejected(self) :
            _l=len(self.sci_rbnspectra_extr.skyBySNR_ind[self.sci_rbnspectra_extr.skyFibIsSelected])
            self.sci_rbnspectra_extr.skyRejected=numpy.ones(_l, dtype=bool)
            _s=self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.reject-thresh')].paramChangeCtrl.GetValue()
            if _s is not '' :
                self.skyRejectThreshholds=numpy.array(_s.split(","), dtype='|S30').astype(numpy.int)
                if self.skyRejectHighestSNRfibCheckBox.GetValue() :
                    i=0
                    for t in self.skyRejectThreshholds :
                        if _l >= t :
                            self.sci_rbnspectra_extr.skyRejected[i]=False
                            i+=1
                self.setNumFibsAvailable()

        def setNumFibsAvailable(self) :
            if self.skyNumSkyFibs is not None :
                cs=self.skyNumSkyFibs.GetCurrentSelection()
                # number of sky fibres in manual selection...
                nsfms=len(self.sci_rbnspectra_extr.skyListSNR[self.sci_rbnspectra_extr.skyFibIsSelected][self.sci_rbnspectra_extr.skyRejected])
                self.skyNumSkyFibs.Clear()
                if self.sci_rbnspectra_extr.skyListSNR[self.sci_rbnspectra_extr.skyFibIsSelected][self.sci_rbnspectra_extr.skyRejected] != [] :
                    for i in range(1,nsfms+1) :
                        self.skyNumSkyFibs.Append("%d" %(i))
                    if cs < 0 or cs >= nsfms or not self.skyNumSkyFibsHasBeenSet :
                        self.skyNumSkyFibs.SetSelection(nsfms-1)
                    else :
                        self.skyNumSkyFibs.SetSelection(cs)
                self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.numFibs')].paramChangeCtrl.SetValue(self.skyNumSkyFibs.GetCurrentSelection()+1)


        def onSkyFibManualSelectionChanged(self, event):
            """
            This event handler is called whenever an item is selected or unselected
            in the checkBoxList. We need to update the method...
            """
            self.setSkyFibIsSelected()
            if numpy.any(numpy.logical_not(self.sci_rbnspectra_extr.skyFibIsSelected)) :
                self.params_by_name[(
                    self.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list'
                        )].paramChangeCtrl.SetValue(
                            re.sub(r'\s',r'',','.join(
                                self.sci_rbnspectra_extr.objectList[
                                    self.sci_rbnspectra_extr.skyBySNR_ind[
                                        numpy.where(numpy.logical_not(self.sci_rbnspectra_extr.skyFibIsSelected))
                                        ]
                                    ]
                                ))
                            )
                '''
                    str(x).strip() for x in self.sci_rbnspectra_extr.objectList[
                        self.sci_rbnspectra_extr.skyBySNR_ind[
                            self.sci_rbnspectra_extr.skyFibIsSelected == False
                        ]
                    ]
                '''
            else:
                self.params_by_name[(
                    self.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list'
                        )].paramChangeCtrl.SetValue("")
            self.setNumFibsAvailable()
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamApplyTo(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            self.setSkyChoiceFromParamCtrl((self.SCI_STD["recipe"],'giraffe.skysub.applyTo'),self.skyApplyToAllOrOne)
            self.enableCheckBoxes()
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamMethod(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            self.setSkyChoiceFromParamCtrl((self.SCI_STD["recipe"],'giraffe.skysub.method'),self.skyApplicationMethod)
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamStatistic(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            self.setSkyChoiceFromParamCtrl((self.SCI_STD["recipe"],'giraffe.skysub.statistic'),self.skyCompMethod)
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamNumFibs(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            self.setSkyChoiceFromParamCtrl((self.SCI_STD["recipe"],'giraffe.skysub.numFibs'),self.skyNumSkyFibs)
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamReject(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            self.setSkyCheckBoxFromParamCtrl((self.SCI_STD["recipe"],'giraffe.skysub.reject'),self.skyRejectHighestSNRfibCheckBox)
            self.setSkyRejected()
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyParamRejectThresh(self, event):
            """
            This event handler is called whenever the All or One changes from
            the drop down list. We need to update the method...
            """
            try:
                ## Following line is just to test format of string, to make sure it is a list of integers...
                _il=numpy.array(self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.reject-thresh')].paramChangeCtrl.GetValue().split(","), dtype='|S30').astype(numpy.int)
                self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.reject-thresh')].setValue(self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.reject-thresh')].paramChangeCtrl.GetValue())
                self.setSkyRejected()
                self._computeSky()
                self.plotProductsGraphics()
                self.figure.canvas.draw()
            except:
                # If not valid should make the list turn red or something...
                pass

        def onSkyParamObjIgnoreList(self, event):
            """
            This event handler is called whenever the Object Ignore List is edited.
            """
            self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list')].setValue(self.params_by_name[(self.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list')].paramChangeCtrl.GetValue())
            self.setSkyFibIsSelectedFromParam()
            self._computeSky()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSkyFibManualSelectionDoubleClicked(self, event):
            """
            This event handler is called whenever one of the sky fibres in the
            SkyFibremanualSelection CheckListBox is double clicked.
            Need to update the selected fibre and then update the plots.
            """
            i=self.skyFibManualSelection.GetSelections()[0]
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.skyBySNR_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()


        def onClickNextSky(self, event):
            """
            This event handler is called whenever the 'next' button is clicked
            for the Sky selection. Need to update the selected file and then
            update the plots.
            """
            if self.sci_rbnspectra_extr.fib_id-1 in self.sci_rbnspectra_extr.skyBySNR_ind :
                i=list(range(len(self.sci_rbnspectra_extr.skyBySNR_ind)))[numpy.where(self.sci_rbnspectra_extr.skyBySNR_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
                if i < len(self.sci_rbnspectra_extr.skyBySNR_ind)-1 :
                    i+=1
            else :
                i=0
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.skyBySNR_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()


        def onClickPreviousSky(self, event):
            """
            This event handler is called whenever the 'previous' button is
            clicked for the file selection. Need to update the file choice and
            plots, like for onClickNextSky.
            """
            if self.sci_rbnspectra_extr.fib_id-1 in self.sci_rbnspectra_extr.skyBySNR_ind :
                i=list(range(len(self.sci_rbnspectra_extr.skyBySNR_ind)))[numpy.where(self.sci_rbnspectra_extr.skyBySNR_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
                if i > 0 :
                    i-=1
            else :
                i=0
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.skyBySNR_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onClickNextObjByName(self, event):
            """
            This event handler is called whenever the 'next' button is clicked
            for the file selection. Need to update the selected file and then
            update the plots.
            """
            i=list(range(len(self.sci_rbnspectra_extr.objectSkyByName_ind)))[numpy.where(self.sci_rbnspectra_extr.objectSkyByName_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
            if i < len(self.sci_rbnspectra_extr.objectSkyByName_ind)-1 :
                i+=1
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.objectSkyByName_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()


        def onClickPreviousObjByName(self, event):
            """
            This event handler is called whenever the 'previous' button is
            clicked for the file selection. Need to update the file choice and
            plots, like for onClickNextObjByName.
            """
            i=list(range(len(self.sci_rbnspectra_extr.objectSkyByName_ind)))[numpy.where(self.sci_rbnspectra_extr.objectSkyByName_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
            if i > 0 :
                i+=-1
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.objectSkyByName_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onClickNextObjBySNR(self, event):
            """
            This event handler is called whenever the 'next' button is clicked
            for the file selection. Need to update the selected file and then
            update the plots.
            """
            i=list(range(len(self.sci_rbnspectra_extr.objectSkyBySNR_ind)))[numpy.where(self.sci_rbnspectra_extr.objectSkyBySNR_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
            if i < len(self.sci_rbnspectra_extr.objectSkyBySNR_ind)-1 :
                i+=1
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.objectSkyBySNR_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()


        def onClickPreviousObjBySNR(self, event):
            """
            This event handler is called whenever the 'previous' button is
            clicked for the file selection. Need to update the file choice and
            plots, like for onClickNextObjBySNR.
            """
            i=list(range(len(self.sci_rbnspectra_extr.objectSkyBySNR_ind)))[numpy.where(self.sci_rbnspectra_extr.objectSkyBySNR_ind == self.sci_rbnspectra_extr.fib_id-1)[0][0]]
            if i > 0 :
                i+=-1
            self.sci_rbnspectra_extr.selectObject(self.sci_rbnspectra_extr.objectSkyBySNR_ind[i]+1)
            self.setObjectByNameAndBySNRFromFIB_ID()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onNoCRC_DisplaySignalCheckBox(self, event):
            """
            This event handler is called whenever the NoCRC_display Signal check box is toggled.
            """
            self.sci_rbnspectra_extr.NoCRC_displaySignal=self.NoCRC_displaySignalCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onNoCRC_DisplaySignalMinusSkyCheckBox(self, event):
            """
            This event handler is called whenever the NoCRC_display SignalMinusSky check box is toggled.
            """
            self.sci_rbnspectra_extr.NoCRC_displaySignalMinusSky=self.NoCRC_displaySignalMinusSkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onNoCRC_DisplaySkyCheckBox(self, event):
            """
            This event handler is called whenever the NoCRC_display Sky check box is toggled.
            """
            self.sci_rbnspectra_extr.NoCRC_displaySky=self.NoCRC_displaySkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onNoCRC_DisplayErrCheckBox(self, event):
            """
            This event handler is called whenever the NoCRC_display Error check box is toggled.
            """
            self.sci_rbnspectra_extr.NoCRC_displayErr=self.NoCRC_displayErrCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onAS_DisplaySignalCheckBox(self, event):
            """
            This event handler is called whenever the AS_display Signal check box is toggled.
            """
            self.sci_rbnspectra_extr.AS_displaySignal=self.AS_displaySignalCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onAS_DisplaySignalMinusSkyCheckBox(self, event):
            """
            This event handler is called whenever the AS_display SignalMinusSky check box is toggled.
            """
            self.sci_rbnspectra_extr.AS_displaySignalMinusSky=self.AS_displaySignalMinusSkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onAS_DisplaySkyCheckBox(self, event):
            """
            This event handler is called whenever the AS_display Sky check box is toggled.
            """
            self.sci_rbnspectra_extr.AS_displaySky=self.AS_displaySkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onAS_DisplayErrCheckBox(self, event):
            """
            This event handler is called whenever the AS_display Err check box is toggled.
            """
            self.sci_rbnspectra_extr.AS_displayErr=self.AS_displayErrCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onPC_DisplaySignalCheckBox(self, event):
            """
            This event handler is called whenever the PC_display Signal check box is toggled.
            """
            self.sci_rbnspectra_extr.PC_displaySignal=self.PC_displaySignalCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onPC_DisplaySignalMinusSkyCheckBox(self, event):
            """
            This event handler is called whenever the PC_display SignalMinusSky check box is toggled.
            """
            self.sci_rbnspectra_extr.PC_displaySignalMinusSky=self.PC_displaySignalMinusSkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onPC_DisplaySkyCheckBox(self, event):
            """
            This event handler is called whenever the PC_display Sky check box is toggled.
            """
            self.sci_rbnspectra_extr.PC_displaySky=self.PC_displaySkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onPC_DisplayErrCheckBox(self, event):
            """
            This event handler is called whenever the PC_display Err check box is toggled.
            """
            self.sci_rbnspectra_extr.PC_displayErr=self.PC_displayErrCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onMED_DisplaySignalCheckBox(self, event):
            """
            This event handler is called whenever the MED_display Signal check box is toggled.
            """
            self.sci_rbnspectra_extr.MED_displaySignal=self.MED_displaySignalCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onMED_DisplaySignalMinusSkyCheckBox(self, event):
            """
            This event handler is called whenever the MED_display SignalMinusSky check box is toggled.
            """
            self.sci_rbnspectra_extr.MED_displaySignalMinusSky=self.MED_displaySignalMinusSkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onMED_DisplaySkyCheckBox(self, event):
            """
            This event handler is called whenever the MED_display Sky check box is toggled.
            """
            self.sci_rbnspectra_extr.MED_displaySky=self.MED_displaySkyCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onMED_DisplayErrCheckBox(self, event):
            """
            This event handler is called whenever the MED_display Err check box is toggled.
            """
            self.sci_rbnspectra_extr.MED_displayErr=self.MED_displayErrCheckBox.GetValue()
            self.plotProductsGraphics()
            self.figure.canvas.draw()


        def setSkyChoiceFromParam(self, p, w) :
            if p in self.params_by_name :
                ind=w.FindString(str(self.params_by_name[p].parameter.value))
                if ind >= 0 :
                    w.SetSelection(ind)
                else :
                    # Should make this a pop-up in interactive mode...
                    print(("'%s' not a valid option for %s" %(self.params_by_name[p].parameter.value,self.params_by_name[p].parameter.displayName)))
                    pass
            else :
                print(("%s not found in parameters" %str(p)))
                pass

        def setSkyChoiceFromParamCtrl(self, p, w) :
            if p in self.params_by_name :
                self.params_by_name[p].setValue(None)
                self.setSkyChoiceFromParam(p, w)
            else :
                print(("%s not found in parameters" %str(p)))
                pass

        def setSkyCheckBoxFromParam(self, p, w) :
            if p in self.params_by_name :
                if self.params_by_name[p].parameter.value in ( True, False ) :
                    w.SetValue(self.params_by_name[p].parameter.value)
                else :
                    # Should make this a pop-up in interactive mode...
                    print(("'%s' not a valid option for %s" %(self.params_by_name[p].parameter.value,self.params_by_name[p].parameter.displayName)))
                    pass
            else :
                print(("%s not found in parameters" %str(p)))
                pass

        def setSkyCheckBoxFromParamCtrl(self, p, w) :
            if p in self.params_by_name :
                self.params_by_name[p].setValue(None)
                self.setSkyCheckBoxFromParam(p, w)
            else :
                print(("%s not found in parameters" %str(p)))
                pass
            

        ################################################################################
        ## Following three functions not currently used, leftovers from the original esotk...
        def onClickNextExt(self, event):
            """
            This event handler is called whenever the "next" button is clicked
            for extensions. Need to update the slider, current extension ID and
            then update the plots.
            """
            val = self.extSlider.GetValue() + 1
            if val > self.extSlider.GetMax():
                return
            self.extSlider.SetValue(val)
            self.ext_id = val
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onClickPreviousExt(self, event):
            """
            This event handler is called whenever the "previous" button is
            clicked for extensions. Need to update the slider and plots, like
            for onClickNextExt.
            """
            val = self.extSlider.GetValue() - 1
            if val < self.extSlider.GetMin():
                return
            self.extSlider.SetValue(val)
            self.ext_id = val
            self.plotProductsGraphics()
            self.figure.canvas.draw()

        def onSliderChange(self, event):
            """
            This event handler is called whenever the slider position changes.
            We need to update the current extension ID and the plots.
            """
            self.ext_id = event.GetPosition()
            self.plotProductsGraphics()
            self.figure.canvas.draw()
        ## Above three functions not currently used, leftovers from the original esotk...
        ################################################################################


    class ReflexInteractiveWxApp(reflex_interactive_gui.ReflexInteractiveWxApp):
        """
        This class overrides the onMotion method and adds a 'Display ESOREX log' button...
        """

        def onMotion(self, event):
            if event.inaxes:
                #for attr in dir(self.dataPlotManager):
                #    print attr, '=>', getattr(self.dataPlotManager, attr)
                str=""
                if hasattr(self.dataPlotManager,"subplot_sci_rbnspectra") :
                    if event.inaxes in [self.dataPlotManager.subplot_sci_rbnspectra,] :
                        i=int(event.xdata-0.5)
                        OBJECT = self.dataPlotManager.sci_rbnspectra_extr.objectList[i]
                        FPS = self.dataPlotManager.sci_rbnspectra_extr.FPSList[i]
                        SSN = self.dataPlotManager.sci_rbnspectra_extr.SSNList[i]
                        OBJECT_SNR = self.dataPlotManager.sci_rbnspectra_extr.SNRarr[self.dataPlotManager.CRCM][i]
                        str=" %s, S/N = %5.2f [FPS/SSN=%d/%d, col=%d]" %(OBJECT.strip(),OBJECT_SNR,FPS,SSN,i+1)
                self.statusBar.SetStatusText(event.inaxes.format_coord(event.xdata, event.ydata)+str)
            else:
                self.statusBar.SetStatusText((''), 0)


        def bindXrcObjects(self):
            self.Bind(wx.EVT_BUTTON, self.onCont, id=xrc.XRCID('contBtn'))
            self.Bind(wx.EVT_BUTTON, self.onRepeat, id=xrc.XRCID('repeatBtn'))
            self.Bind(wx.EVT_BUTTON, self.onLog, id=xrc.XRCID('logBtn'))
            self.Bind(wx.EVT_BUTTON, self.onHelp, id=xrc.XRCID('helpBtn'))
            self.Bind(wx.EVT_CHECKBOX, self.onSetDisable, self.setDisableCheck)
            self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.onFileInfoPaneChanged)
            self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onItemSelected)

            if(self.inter_app.is_init_sop_enable()):
                self.Bind(wx.EVT_CHECKBOX, self.onSetInitSop, self.setInitSopCheck)

        def onLog(self, event):
            #for attr in dir(self.dataPlotManager.sci_rbnspectra_extr.extractedscience):
            #    print attr, '=>', getattr(self.dataPlotManager.sci_rbnspectra_extr.extractedscience, attr)
            log_file = None
            try :
                log_file = open(self.dataPlotManager.esorex_log_file_name)
            except :
                pass
            if log_file is not None :
                log = log_file.read()
                log_file.close()
                import tkinter as tk
                from tkinter.scrolledtext import ScrolledText
                self.root = tk.Tk()
                self.root.title("esorex log file: %s" %(self.dataPlotManager.esorex_log_file_name))
                S = tk.Scrollbar(self.root)
                T = tk.Text(self.root, height=24, width=80)
                S.pack(side=tk.RIGHT, fill=tk.Y)
                T.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.Y)
                S.config(command=T.yview)
                T.config(yscrollcommand=S.set)

                T.insert(tk.END, log)
                tk.mainloop()
            else :
                print(("Failed to open esorex logfile %s" %(self.dataPlotManager.esorex_log_file_name)))



    class PipelineInteractiveApp(reflex_interactive_app.PipelineInteractiveApp, object):
        """
        This class overrides the showGUI method to bind the event handlers for
        the customised GUI layout provided by the esotk_giraf_sci_gui.xrc file.
        """

        def dont__init__(self, enable_init_sop=False):
            ## Copy/Paste from /opt/local/share/esoreflex-2.9.0/esoreflex/python/reflex_interactive_app.py
            reflex.ReflexIOParser.__init__(self)
            self.enable_init_sop = enable_init_sop
            # define inputs (Note: you must define at least long option)
            self.add_input("-i", "--in_sof", dest="in_sof", type='string')
            self.add_input("-p", "--in_sop", dest="in_sop", type='string')
            self.add_input("-g", "--in_sof_rec_orig",
                           dest="in_sof_rec_orig", type='string')
            self.add_input("-e", "--enable", dest="enable", default='false')
            ## SkySubtraction input parameters...
            self.add_input("--skyOptions", dest="skyOptions", type='string')            

            # define outputs (Note: you must define at least long option)
            self.add_output("-o", "--out_sof", dest="out_sof")
            self.add_output("-q", "--out_sop", dest="out_sop")
            self.add_output("-f", "--out_sof_loop", dest="out_sof_loop")
            self.add_output("-l", "--out_sop_loop", dest="out_sop_loop")
            self.add_output("-c", "--iteration_complete",
                            dest="iteration_complete")
            self.add_output("-s", "--set_enable", dest="set_enable")
            if(enable_init_sop):
                self.add_output("-n", "--set_init_sop", dest="set_init_sop")
    
            # get inputs from the command line
            self.inputs = self.get_inputs()
            # get output variables
            self.outputs = self.get_outputs()
    
            # Initializing state
            self.continue_mode = None
            self.repeat_mode = None
            self.next_iteration_enable = None
            self.set_init_sop = False
    
            # Input validation
            if not (hasattr(self.inputs,'in_sof') and hasattr(self.inputs,'in_sop')
                and hasattr(self.inputs,'in_sof_rec_orig') ) :
                    self.error("Options --in_sof, --in_sop and --in_sof_rec_orig "
                           "are needed")
            if self.inputs.enable.lower() not in ['true', 'false']:
                self.error("Invalid --enable option. Use true or false")
            #    if on_sof is not a sof.....
    
            self.gui_enabled = self.inputs.enable.lower() == 'true'
            
            
            #super(PipelineInteractiveApp, self).__init__(enable_init_sop)
            #self.add_input("--skyOptions", dest="skyOptions", type='string')
            #self.inputs=self.get_inputs()
            #self.add_option("--skyApplyTo", dest="skyApplyTo", type='string')
            #print(get_attrs(self.inputs))
            #print(get_attrs(self.inputs))
            #self.inputs = self.get_inputs()
            #print(self.inputs.in_sof)
            #print(self.extra_inputs)
            #print(self.extra_inputs.skyApplyTo)
            #print(self.inputs.skyApplyTo)

        def showGUI(self, interactive=True):
            try:
                #from reflex_interactive_gui import ReflexInteractiveWxApp
                from esotk_giraf_sci_gui import ReflexInteractiveWxApp
                self.wxApp = ReflexInteractiveWxApp(self, self.dataPlotManager)
                # Bind event handlers to the additional next button, previous
                # button and file choice widgets.
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickNextFile,
                                id = xrc.XRCID('nextFileButton'))
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickPreviousFile,
                                id = xrc.XRCID('previousFileButton'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onFileChange,
                                id = xrc.XRCID('fileChoice'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onObjectBySNRChange,
                                id = xrc.XRCID('objectBySNR'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onObjectByNameChange,
                                id = xrc.XRCID('objectByName'))
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickSetCuts,
                                id = xrc.XRCID('setCutsSubPlotButton'))
                # Sky Subtraction...
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onSkyApplyToAllOrOne,
                                id = xrc.XRCID('skyApplyToAllOrOne'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onSkyApplicationMethod,
                                id = xrc.XRCID('skyApplicationMethod'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onSkyNumSkyFibs,
                                id = xrc.XRCID('skyNumSkyFibs'))
                self.wxApp.Bind(wx.EVT_CHOICE,
                                self.dataPlotManager.onSkyCompMethod,
                                id = xrc.XRCID('skyCompMethod'))
                self.wxApp.Bind(wx.EVT_CHECKLISTBOX,
                                self.dataPlotManager.onSkyFibManualSelectionChanged,
                                id = xrc.XRCID('skyFibManualSelection'))
                self.wxApp.Bind(wx.EVT_LISTBOX_DCLICK,
                                self.dataPlotManager.onSkyFibManualSelectionDoubleClicked,
                                id = xrc.XRCID('skyFibManualSelection'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onSkyRejectHighestSNRfibCheckBox,
                                id = xrc.XRCID('skyRejectHighestSNRfib'))

                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickNextSky,
                                id = xrc.XRCID('nextSkyButton'))
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickPreviousSky,
                                id = xrc.XRCID('previousSkyButton'))

                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickNextObjByName,
                                id = xrc.XRCID('nextObjByNameButton'))
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickPreviousObjByName,
                                id = xrc.XRCID('previousObjByNameButton'))

                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickNextObjBySNR,
                                id = xrc.XRCID('nextObjBySNRButton'))
                self.wxApp.Bind(wx.EVT_BUTTON,
                                self.dataPlotManager.onClickPreviousObjBySNR,
                                id = xrc.XRCID('previousObjBySNRButton'))

                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onNoCRC_DisplaySignalCheckBox,
                                id = xrc.XRCID('NoCRC_displaySignalCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onNoCRC_DisplaySignalMinusSkyCheckBox,
                                id = xrc.XRCID('NoCRC_displaySignalMinusSkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onNoCRC_DisplaySkyCheckBox,
                                id = xrc.XRCID('NoCRC_displaySkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onNoCRC_DisplayErrCheckBox,
                                id = xrc.XRCID('NoCRC_displayErrCheckBox'))

                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onAS_DisplaySignalCheckBox,
                                id = xrc.XRCID('AS_displaySignalCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onAS_DisplaySignalMinusSkyCheckBox,
                                id = xrc.XRCID('AS_displaySignalMinusSkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onAS_DisplaySkyCheckBox,
                                id = xrc.XRCID('AS_displaySkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onAS_DisplayErrCheckBox,
                                id = xrc.XRCID('AS_displayErrCheckBox'))

                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onPC_DisplaySignalCheckBox,
                                id = xrc.XRCID('PC_displaySignalCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onPC_DisplaySignalMinusSkyCheckBox,
                                id = xrc.XRCID('PC_displaySignalMinusSkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onPC_DisplaySkyCheckBox,
                                id = xrc.XRCID('PC_displaySkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onPC_DisplayErrCheckBox,
                                id = xrc.XRCID('PC_displayErrCheckBox'))

                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onMED_DisplaySignalCheckBox,
                                id = xrc.XRCID('MED_displaySignalCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onMED_DisplaySignalMinusSkyCheckBox,
                                id = xrc.XRCID('MED_displaySignalMinusSkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onMED_DisplaySkyCheckBox,
                                id = xrc.XRCID('MED_displaySkyCheckBox'))
                self.wxApp.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onMED_DisplayErrCheckBox,
                                id = xrc.XRCID('MED_displayErrCheckBox'))


                # Bind event handlers to the additional next button, previous
                # button and slider for extension selection.
                #self.wxApp.Bind(wx.EVT_BUTTON,
                #                self.dataPlotManager.onClickNextExt,
                #                id = xrc.XRCID('nextExtButton'))
                #self.wxApp.Bind(wx.EVT_BUTTON,
                #                self.dataPlotManager.onClickPreviousExt,
                #                id = xrc.XRCID('previousExtButton'))
                #self.wxApp.Bind(wx.EVT_SCROLL_CHANGED,
                #                self.dataPlotManager.onSliderChange,
                #                id = xrc.XRCID('extSlider'))

                # Need to update the slider range and initial value. This has to
                # happen at this point because the dataPlotManager.readFitsData
                # method would have been called by now, with it's n_ext value
                # set.
                #extSlider = xrc.XRCCTRL(self.wxApp.frame, 'extSlider')
                #n_ext = self.dataPlotManager.n_ext
                #if n_ext > 1:
                    # For FITS with multiple extensions the image range is from
                    # extension 1 onwards.
                #    extSlider.SetRange(1, n_ext - 1)
                #else:
                    # For only 1 extension set the range to zero.
                #    extSlider.SetRange(0, 0)
                #extSlider.SetValue(self.dataPlotManager.ext_id)
                #self.dataPlotManager.extSlider = extSlider

                # Need to also update the list of files in the file choice
                # widget. We add a reference to the widget in dataPlotManager so
                # that the event handler methods can access it.
                fileChoice = xrc.XRCCTRL(self.wxApp.frame, 'fileChoice')
                fileChoice.Clear()
                for frame in self.dataPlotManager.science_frames:
                    fileChoice.Append(self.dataPlotManager.frameCRCMLabels[frame.name])
                fileChoice.SetSelection(0)
                self.dataPlotManager.fileChoice = fileChoice
                self.dataPlotManager.CRCM = self.dataPlotManager.fileChoice.GetString(self.dataPlotManager.fileChoice.GetSelection())

                # Sky Subtraction
                # get the 'recipe" parameters into a usable format...
                self.dataPlotManager.params_by_name = dict([((x[1].parameter.recipe, x[1].parameter.name), x[1])
                    for x in self.wxApp.shownParamWidgets])
                
                #print(get_attrs(self.wxApp))
                #print(self.wxApp.shownParamWidgets)
                #for p in self.wxApp.shownParamWidgets :
                #    print(get_attrs(p[1]),p[1].parameter.name)
                #print(self.wxApp.shownParam)
                #for p in self.wxApp.shownParam :
                #    print(p.name,p.value)
                #print(self.dataPlotManager.params_by_name[ (self.dataPlotManager.SCI_STD["recipe"], 'giraffe.skysub.numFibs' ) ])
                #print(self.dataPlotManager.params_by_name[ (self.dataPlotManager.SCI_STD["recipe"], 'giraffe.skysub.numFibs' ) ].value)
                #print(self.dataPlotManager.paramChangeCtrls_by_name[ (self.dataPlotManager.SCI_STD["recipe"], 'giraffe.skysub.numFibs' ) ].GetValue())
                #self.dataPlotManager.paramChangeCtrls_by_name[ (self.dataPlotManager.SCI_STD["recipe"], 'giraffe.skysub.numFibs' ) ].SetValue(0)
                #print(self.dataPlotManager.<paramChangeCtrls_by_name[ (self.dataPlotManager.SCI_STD["recipe"], 'giraffe.skysub.numFibs' ) ].GetValue())

                ## Number of sky fibres in the setup
                nsf=len(self.dataPlotManager.sci_rbnspectra_extr.skyListSNR)
                ## Reject hishest S/N sky fib?
                skyRejectHighestSNRfibCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'skyRejectHighestSNRfib')
                if skyRejectHighestSNRfibCheckBox is not None :
                    skyRejectHighestSNRfibCheckBox.SetValue(True)
                    self.dataPlotManager.setSkyCheckBoxFromParam((self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.reject'), skyRejectHighestSNRfibCheckBox )
                    self.dataPlotManager.skyRejectHighestSNRfibCheckBox = skyRejectHighestSNRfibCheckBox
                    self.dataPlotManager.setSkyRejected()
                ## Manual selection
                skyFibManualSelection = xrc.XRCCTRL(self.wxApp.frame, 'skyFibManualSelection')
                skyFibManualSelection.Clear()
                j=0
                if nsf > 0 :
                    for i in self.dataPlotManager.sci_rbnspectra_extr.skyBySNR_ind :
                        OBJECT = self.dataPlotManager.sci_rbnspectra_extr.objectList[i]
                        FPS = self.dataPlotManager.sci_rbnspectra_extr.FPSList[i]
                        SSN = self.dataPlotManager.sci_rbnspectra_extr.SSNList[i]
                        OBJECT_SNR = self.dataPlotManager.sci_rbnspectra_extr.SNRarr[self.dataPlotManager.CRCM][i]
                        str=" %s, S/N = %5.2f [FPS/SSN=%d/%d, col=%d]" %(OBJECT.strip(),OBJECT_SNR,FPS,SSN,i+1)
                        skyFibManualSelection.Append(str)
                        #skyFibManualSelection.Check(j, True)
                        j+=1
                    self.dataPlotManager.skyFibManualSelection = skyFibManualSelection
                    self.dataPlotManager.setSkyFibIsSelected()
                    self.dataPlotManager.setSkyFibIsSelectedFromParam()
                ## Number of fibres
                skyNumSkyFibs = xrc.XRCCTRL(self.wxApp.frame, 'skyNumSkyFibs')
                skyNumSkyFibs.Clear()
                self.dataPlotManager.skyNumSkyFibs = skyNumSkyFibs
                self.dataPlotManager.setNumFibsAvailable()
                ## Apply to All science fibres or just the current one selected...
                ## <tooltip>Select the method to apply the current sky calc to all science fibres or one-by-one,...</tooltip>
                skyApplyToAllOrOne = xrc.XRCCTRL(self.wxApp.frame, 'skyApplyToAllOrOne')
                skyApplyToAllOrOne.Clear()
                skyApplyToAllOrOne.Append("None")
                if nsf > 0 :
                    skyApplyToAllOrOne.Append("All")
                    #skyApplyToAllOrOne.Append("One")
                    skyApplyToAllOrOne.SetSelection(1)
                else :
                    skyApplyToAllOrOne.SetSelection(0)
                self.dataPlotManager.setSkyChoiceFromParam( (self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.applyTo'), skyApplyToAllOrOne )
                self.dataPlotManager.skyApplyToAllOrOne = skyApplyToAllOrOne

                ## Application Method
                ## <tooltip>Select the method to apply the sky from the drop down list, All-fibs, N-fibs, nearest-N-fibs, etc</tooltip>
                skyApplicationMethod = xrc.XRCCTRL(self.wxApp.frame, 'skyApplicationMethod')
                skyApplicationMethod.Clear()
                if nsf > 0 :
                    skyApplicationMethod.Append("All Sky Fibres")
                    skyApplicationMethod.SetSelection(0)
                else :
                    skyApplicationMethod.Append("None")
                    skyApplicationMethod.SetSelection(0)
                if nsf > 1 :
                    skyApplicationMethod.Append("Best <N> Fibres by S/N")
                    skyApplicationMethod.Append("Nearest <N> Fibres")
                #skyApplicationMethod.Append("Improves S/N")
                self.dataPlotManager.setSkyChoiceFromParam( (self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.method'), skyApplicationMethod )
                self.dataPlotManager.skyApplicationMethod = skyApplicationMethod
                ## Apply to All science fibres or just the current one selected...
                ## <tooltip>Select the method to compute the sky from the drop down list, mean, median, mode,...</tooltip>
                skyCompMethod = xrc.XRCCTRL(self.wxApp.frame, 'skyCompMethod')
                skyCompMethod.Clear()
                skyCompMethod.Append("Median")
                skyCompMethod.Append("Mean")
                #skyCompMethod.Append("Mode")
                skyCompMethod.SetSelection(0)
                self.dataPlotManager.setSkyChoiceFromParam( (self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.statistic'), skyCompMethod )
                self.dataPlotManager.skyCompMethod = skyCompMethod

                self.dataPlotManager._computeSky()
                
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.applyTo')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_COMBOBOX,
                                self.dataPlotManager.onSkyParamApplyTo)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.method')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_COMBOBOX,
                                self.dataPlotManager.onSkyParamMethod)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.statistic')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_COMBOBOX,
                                self.dataPlotManager.onSkyParamStatistic)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.numFibs')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_TEXT,
                                self.dataPlotManager.onSkyParamNumFibs)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.reject')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_CHECKBOX,
                                self.dataPlotManager.onSkyParamReject)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.reject-thresh')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_TEXT,
                                self.dataPlotManager.onSkyParamRejectThresh)
                p=(self.dataPlotManager.SCI_STD["recipe"],'giraffe.skysub.obj-ignore-list')
                if p in self.dataPlotManager.params_by_name :
                    self.dataPlotManager.params_by_name[p].paramChangeCtrl.Bind(wx.EVT_TEXT,
                                self.dataPlotManager.onSkyParamObjIgnoreList)

                ########################################/opt/local/etc/esoreflex_default_recipe_config.rc########################################
                ## no CRC
                ## Display the signal plot?
                NoCRC_displaySignalCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'NoCRC_displaySignalCheckBox')
                NoCRC_displaySignalCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.NoCRC_displaySignal)
                self.dataPlotManager.NoCRC_displaySignalCheckBox = NoCRC_displaySignalCheckBox
                self.dataPlotManager.NoCRC_displaySignalCheckBox.Enable("NONE" in list(self.dataPlotManager.sf.keys()))
                ## Display the signal-sky plot?
                NoCRC_displaySignalMinusSkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'NoCRC_displaySignalMinusSkyCheckBox')
                NoCRC_displaySignalMinusSkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.NoCRC_displaySignalMinusSky)
                self.dataPlotManager.NoCRC_displaySignalMinusSkyCheckBox = NoCRC_displaySignalMinusSkyCheckBox
                self.dataPlotManager.NoCRC_displaySignalMinusSkyCheckBox.Enable("NONE" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the sky plot?
                NoCRC_displaySkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'NoCRC_displaySkyCheckBox')
                NoCRC_displaySkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.NoCRC_displaySky)
                self.dataPlotManager.NoCRC_displaySkyCheckBox = NoCRC_displaySkyCheckBox
                self.dataPlotManager.NoCRC_displaySkyCheckBox.Enable("NONE" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the errors?
                NoCRC_displayErrCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'NoCRC_displayErrCheckBox')
                NoCRC_displayErrCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.NoCRC_displayErr)
                self.dataPlotManager.NoCRC_displayErrCheckBox = NoCRC_displayErrCheckBox
                self.dataPlotManager.NoCRC_displayErrCheckBox.Enable("NONE" in list(self.dataPlotManager.sf.keys()))

                ### astroSCRAPPY
                ## Display the signal plot?
                AS_displaySignalCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'AS_displaySignalCheckBox')
                AS_displaySignalCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.AS_displaySignalMinusSky)
                self.dataPlotManager.AS_displaySignalCheckBox = AS_displaySignalCheckBox
                self.dataPlotManager.AS_displaySignalCheckBox.Enable("ASTROSCRAPPY" in list(self.dataPlotManager.sf.keys()))
                ## Display the signal-sky plot?
                AS_displaySignalMinusSkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'AS_displaySignalMinusSkyCheckBox')
                AS_displaySignalMinusSkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.AS_displaySignalMinusSky)
                self.dataPlotManager.AS_displaySignalMinusSkyCheckBox = AS_displaySignalMinusSkyCheckBox
                self.dataPlotManager.AS_displaySignalMinusSkyCheckBox.Enable("ASTROSCRAPPY" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the sky plot?
                AS_displaySkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'AS_displaySkyCheckBox')
                AS_displaySkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.AS_displaySky)
                self.dataPlotManager.AS_displaySkyCheckBox = AS_displaySkyCheckBox
                self.dataPlotManager.AS_displaySkyCheckBox.Enable("ASTROSCRAPPY" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the errors?
                AS_displayErrCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'AS_displayErrCheckBox')
                AS_displayErrCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.AS_displayErr)
                self.dataPlotManager.AS_displayErrCheckBox = AS_displayErrCheckBox
                self.dataPlotManager.AS_displayErrCheckBox.Enable("ASTROSCRAPPY" in list(self.dataPlotManager.sf.keys()))

                ## pyCosmic
                ## Display the signal plot?
                PC_displaySignalCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'PC_displaySignalCheckBox')
                PC_displaySignalCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.PC_displaySignal)
                self.dataPlotManager.PC_displaySignalCheckBox = PC_displaySignalCheckBox
                self.dataPlotManager.PC_displaySignalCheckBox.Enable("PYCOSMIC" in list(self.dataPlotManager.sf.keys()))
                ## Display the signal-sky plot?
                PC_displaySignalMinusSkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'PC_displaySignalMinusSkyCheckBox')
                PC_displaySignalMinusSkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.PC_displaySignalMinusSky)
                self.dataPlotManager.PC_displaySignalMinusSkyCheckBox = PC_displaySignalMinusSkyCheckBox
                self.dataPlotManager.PC_displaySignalMinusSkyCheckBox.Enable("PYCOSMIC" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the sky plot?
                PC_displaySkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'PC_displaySkyCheckBox')
                PC_displaySkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.PC_displaySky)
                self.dataPlotManager.PC_displaySkyCheckBox = PC_displaySkyCheckBox
                self.dataPlotManager.PC_displaySkyCheckBox.Enable("PYCOSMIC" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the errors?
                PC_displayErrCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'PC_displayErrCheckBox')
                PC_displayErrCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.PC_displayErr)
                self.dataPlotManager.PC_displayErrCheckBox = PC_displayErrCheckBox
                self.dataPlotManager.PC_displayErrCheckBox.Enable("PYCOSMIC" in list(self.dataPlotManager.sf.keys()))

                ## Median
                ## Display the signal plot?
                MED_displaySignalCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'MED_displaySignalCheckBox')
                MED_displaySignalCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.MED_displaySignal)
                self.dataPlotManager.MED_displaySignalCheckBox = MED_displaySignalCheckBox
                self.dataPlotManager.MED_displaySignalCheckBox.Enable("MEDIAN" in list(self.dataPlotManager.sf.keys()))
                ## Display the signal-sky plot?
                MED_displaySignalMinusSkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'MED_displaySignalMinusSkyCheckBox')
                MED_displaySignalMinusSkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.MED_displaySignalMinusSky)
                self.dataPlotManager.MED_displaySignalMinusSkyCheckBox = MED_displaySignalMinusSkyCheckBox
                self.dataPlotManager.MED_displaySignalMinusSkyCheckBox.Enable("MEDIAN" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the sky plot?
                MED_displaySkyCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'MED_displaySkyCheckBox')
                MED_displaySkyCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.MED_displaySky)
                self.dataPlotManager.MED_displaySkyCheckBox = MED_displaySkyCheckBox
                self.dataPlotManager.MED_displaySkyCheckBox.Enable("MEDIAN" in list(self.dataPlotManager.sf.keys()) and self.dataPlotManager.skyApplyToAllOrOne.GetString(self.dataPlotManager.skyApplyToAllOrOne.GetSelection()) != "None")
                ## Display the errors?
                MED_displayErrCheckBox = xrc.XRCCTRL(self.wxApp.frame, 'MED_displayErrCheckBox')
                MED_displayErrCheckBox.SetValue(self.dataPlotManager.sci_rbnspectra_extr.MED_displayErr)
                self.dataPlotManager.MED_displayErrCheckBox = MED_displayErrCheckBox
                self.dataPlotManager.MED_displayErrCheckBox.Enable("MEDIAN" in list(self.dataPlotManager.sf.keys()))

                # Target selectors...
                ## target selector list ordered by name
                self.dataPlotManager.objectByName = xrc.XRCCTRL(self.wxApp.frame, 'objectByName')
                ## target selector list ordered by S/N
                self.dataPlotManager.objectBySNR = xrc.XRCCTRL(self.wxApp.frame, 'objectBySNR')
                self.dataPlotManager.updateFrameStrings()

                if interactive :
                    self.wxApp.MainLoop()

            except (ImportError, NameError) as e:
                sys.stderr.write("Error importing modules: {0}\n".format(e))
                raise



except ImportError:
    import_success = False
    PipelineInteractiveApp = reflex_interactive_app.PipelineInteractiveApp

    class BaseDataPlotterManager(object):
        """
        Empty class definition to prevent ImportError
        """
        def __init__(self):
            pass

def runInteractiveApp(dataManagerClass):
    """
    This helper function runs the python interactive application to provide an
    interactive GUI for viewing esotk fringing workflow output.
    One must provide the DataPlotterManager class to use as the data interface
    in the 'dataManagerClass' parameter. The DataPlotterManager class should
    inherit from BaseDataPlotterManager to automatically provide necessary GUI
    event handlers and support methods.
    """

    # Create interactive application.
    interactive_app = PipelineInteractiveApp(enable_init_sop = True)

    # Check if import failed or not.
    if import_success == False:
        # Note: we have to print this message after interactive_app is created
        # or else this message will not be shown in the Reflex error message.
        sys.stderr.write("Error importing one of the modules wx, reflex," \
                         " numpy, astropy.io/pyfits, pylab or matplotlib.\n")
        interactive_app.setEnableGUI(False)
        sys.exit(1)

    # Open the interactive window if enabled.
    # Get the specific functions for this window.
    dataPlotManager = dataManagerClass()
    interactive_app.setPlotManager(dataPlotManager)
    interactive_app.showGUI(interactive=interactive_app.isGUIEnabled())
    if not interactive_app.isGUIEnabled() :
        interactive_app.passProductsThrough()
    if interactive_app.repeat_mode is None or not interactive_app.repeat_mode :
        for CRCM in list(dataPlotManager.sf.keys()) :
            sHDUlist=None
            if dataPlotManager.sci_rbnspectra_extr.sky[CRCM] is not None :
                # Add sky extension to science frame file
                sHDUlist=pyfits.open(dataPlotManager.sf[CRCM].name, mode='update')
                if "SKYBACK" in sHDUlist :
                    sHDUlist["SKYBACK"].data=dataPlotManager.sci_rbnspectra_extr.sky[CRCM][0]
                    pass
                else :
                    sHDUlist.append(pyfits.ImageHDU(dataPlotManager.sci_rbnspectra_extr.sky[CRCM][0]))
                    sHDUlist[-1].header['EXTNAME']="SKYBACK"
                    pass
                dataPlotManager.sci_rbnspectra_extr.updateSkyHeader(sHDUlist["SKYBACK"], CRCM=CRCM)
                # Add signal - sky extension to science frame file
                if "FLUX_SKY_SUB" in sHDUlist :
                    sHDUlist["FLUX_SKY_SUB"].data=dataPlotManager.sci_rbnspectra_extr.extractedscience[CRCM].image-dataPlotManager.sci_rbnspectra_extr.sky[CRCM][0]
                    pass
                else :
                    sHDUlist.append(
                        pyfits.ImageHDU(dataPlotManager.sci_rbnspectra_extr.extractedscience[CRCM].image-dataPlotManager.sci_rbnspectra_extr.sky[CRCM][0])
                        )
                    sHDUlist[-1].header['EXTNAME']="FLUX_SKY_SUB"
                    pass
                dataPlotManager.sci_rbnspectra_extr.updateSkyHeader(sHDUlist["FLUX_SKY_SUB"], CRCM=CRCM)
                # Add sky-err extension to error frame file
                eHDUlist=pyfits.open(dataPlotManager.ef[CRCM].name, mode='update')
                if "SKYBACK_ERR" in eHDUlist :
                    eHDUlist["SKYBACK_ERR"].data=dataPlotManager.sci_rbnspectra_extr.sky[CRCM][1]
                    pass
                else :
                    eHDUlist.append(pyfits.ImageHDU(dataPlotManager.sci_rbnspectra_extr.sky[CRCM][1]))
                    eHDUlist[-1].header['EXTNAME']="SKYBACK_ERR"
                    pass
                dataPlotManager.sci_rbnspectra_extr.updateSkyHeader(eHDUlist["SKYBACK_ERR"], CRCM=CRCM)
                # Add signal - sky error extension to science frame file
                if "FLUX_SKY_SUB_ERR" in eHDUlist :
                    eHDUlist["FLUX_SKY_SUB_ERR"].data=dataPlotManager.sci_rbnspectra_extr.extractederrors[CRCM].image-dataPlotManager.sci_rbnspectra_extr.sky[CRCM][1]
                    pass
                else :
                    eHDUlist.append(
                        pyfits.ImageHDU(dataPlotManager.sci_rbnspectra_extr.extractederrors[CRCM].image-dataPlotManager.sci_rbnspectra_extr.sky[CRCM][1])
                        )
                    eHDUlist[-1].header['EXTNAME']="FLUX_SKY_SUB_ERR"
                    pass
                dataPlotManager.sci_rbnspectra_extr.updateSkyHeader(eHDUlist["FLUX_SKY_SUB_ERR"], CRCM=CRCM)
                eHDUlist.close()
            if sHDUlist is not None :
                sHDUlist.close()
        if dataPlotManager.sci_rbnspectra_extr.PLparams[list(dataPlotManager.sf.keys())[0]].get('generate-SDP-format','false') == 'true' :
            # Update SDP product headers with CRC keywords...
            flist={}
            nfs=0
            for CRCM in list(dataPlotManager.sf.keys()) :
                flist[CRCM]=glob.glob(os.path.dirname(dataPlotManager.sf[CRCM].name)+'/science_spectrum_[0-9][0-9][0-9].fits')
                nfs+=len(flist[CRCM])
            i=0
            if interactive_app.isGUIEnabled() :
                progress = wx.ProgressDialog(
                    "Updating SDP-style product files",
                    "Updating the sky columns in the SDP-style product files, please wait...",
                    maximum=100,
                    style=wx.PD_SMOOTH|wx.PD_AUTO_HIDE|wx.PD_REMAINING_TIME
                    )
            for CRCM in list(dataPlotManager.sf.keys()) :
                if not interactive_app.isGUIEnabled() :
                    print(("Updating %s SDP-style product files..." %(CRCM)))
                for F in flist[CRCM] :
                    # We assume that the SDP products contain only one HDU...
                    sdpTbl=Table.read(F)
                    sdpHDUList=pyfits.open(F, mode='update')
                    #sdpHDUList=pyfits.open(F)
                    ## Add CRC headers here...
                    dataPlotManager.sci_rbnspectra_extr.updateSkyHeader(sdpHDUList[0], CRCM=CRCM)
                    dataPlotManager.sci_rbnspectra_extr.copyCRCkwsToHeader(sdpHDUList[0], CRCM=CRCM)
                    FPS=sdpHDUList[0].header["FPS"]
                    if dataPlotManager.sci_rbnspectra_extr.sky is not None :
                        # Add columns for the sky and error on the sky
                        if not "SKYBACK" in list(sdpTbl.keys()) :
                            sdpTbl["SKYBACK"]=sdpTbl["FLUX_REDUCED"]
                        sdpTbl["SKYBACK"][0]=numpy.array(dataPlotManager.sci_rbnspectra_extr.sky[CRCM][0][:,numpy.where(dataPlotManager.sci_rbnspectra_extr.FPSList == FPS)[0][0]])
                        if not "SKYBACK_ERR" in list(sdpTbl.keys()) :
                            sdpTbl["SKYBACK_ERR"]=sdpTbl["ERR_REDUCED"]
                        sdpTbl["SKYBACK_ERR"][0]=numpy.array(dataPlotManager.sci_rbnspectra_extr.sky[CRCM][1][:,numpy.where(dataPlotManager.sci_rbnspectra_extr.FPSList == FPS)[0][0]])

                        # Add columns for the signal-sky and error on the signal-sky
                        if not "FLUX_SKY_SUB" in list(sdpTbl.keys()) :
                            sdpTbl["FLUX_SKY_SUB"]=sdpTbl["FLUX_REDUCED"]
                        sdpTbl["FLUX_SKY_SUB"][0]=sdpTbl["FLUX_REDUCED"]-sdpTbl["SKYBACK"]
                        if not "FLUX_SKY_SUB_ERR" in list(sdpTbl.keys()) :
                            sdpTbl["FLUX_SKY_SUB_ERR"]=sdpTbl["ERR_REDUCED"]
                        sdpTbl["FLUX_SKY_SUB_ERR"][0]=sdpTbl["ERR_REDUCED"]+sdpTbl["SKYBACK_ERR"]
                        '''
                        # Add header keywords
                        TUCD4 = 'phot.flux.density;em.wl;src.net'
                        TUTYP4 = 'eso:Data.FluxAxis.Value'
                        append ";meta.main" to one on the fluxes (and its associated error)...
                        For errors...
                        TUCD3 = 'stat.error;phot.flux.density;em.wl;src.net'
                        TUTYP3 = 'spec:Data.FluxAxis.Accuracy.StatError'

                        '''
                        if False :
                            ## DEBUG:: Check that flux from selected column in extractedscience is identical to the SDP value...
                            if not "FLUX_CHK" in list(sdpTbl.keys()) :
                                sdpTbl["FLUX_CHK"]=sdpTbl["FLUX_REDUCED"]
                            sdpTbl["FLUX_CHK"][0]=numpy.array(dataPlotManager.sci_rbnspectra_extr.extractedscience[CRCM].image[:,numpy.where(dataPlotManager.sci_rbnspectra_extr.FPSList == FPS)[0][0]])
                            sdpTbl["ZERO"]=sdpTbl["FLUX_REDUCED"]-sdpTbl["FLUX_CHK"]
                            #print((numpy.mean(sdpTbl["ZERO"]),numpy.min(sdpTbl["ZERO"]),numpy.max(sdpTbl["ZERO"])))

                        sdpTbl.write("tmp.fits", overwrite=True)
                        thl=pyfits.open("tmp.fits")
                        sdpHDUList[1]=thl[1].copy()
                        thl.close()
                        os.unlink("tmp.fits")
                        
                    sdpHDUList.close()
                    if interactive_app.isGUIEnabled() :
                        progress.Update(int(float(i)/float(nfs)*100))
                    i+=1
                    pass

    # Print outputs. This is parsed by the Reflex python actor to get the
    # results. Do not remove.
    interactive_app.print_outputs()
