# ========================================================================================================
#
# John Pritchard
#
# Developed from esotk_fringe_obj_mask.py by Mark Neeser
#
# A python interactive window for use in the Austrian in-kind
# fringe-correction Reflex workflow.  This interactive window
# shows the results of the fringe-correction pipeline, allowing
# the User to compare the input image with the fringe-corrected
# image, and the fringe map.   From this interactive Actor the
# User can modify pipeline parameters and re-initiate the
# processing.
#
#
#
# ========================================================================================================

import os
import re
import sys
sys.path.append(os.path.dirname(__file__))
from esotk_giraf_gui import BaseDataPlotterManager, runInteractiveApp

try:
    # Import non-standard but needed modules. These must be as subset of what
    # the esotk_fringe_gui module tries to load.
    import reflex
    import pipeline_product
    import pipeline_display
    import matplotlib
    from giraf_plot_common import *

    class DataPlotterManager(BaseDataPlotterManager):

        # This function will read all the columns, images and whatever is needed
        # from the products. The variables , self.plot_x, self.plot_y, etc...
        # are used later in function plotProductsGraphics().
        def readFitsData(self, fitsFiles):
            
            # If fitsFiles doesn't have FF_PSFCENTROID, recipe must have failed, so search for tmp products...
            #Read all the products
            frames = dict()
            for frame in fitsFiles :
                if frame == '' :
                    continue
                category = frame.category
                frames[category] = frame
            self.have_FF_PSFCENTROID=True
            if not "FF_PSFCENTROID" in frames :
                self.have_FF_PSFCENTROID=False
                import glob
                for F in glob.glob('../../gimasterflat_1/latest/*.fits') :
                    HDUlist=pyfits.open(F)
                    try :
                        fitsFiles.append(reflex.FitsFile(os.path.abspath(F), HDUlist[0].header['HIERARCH ESO PRO CATG'], None, None))
                    except :
                        pass
                    HDUlist.close()

            # Control variable to check if the interesting files were found in
            # the input.
            self.flat_frames = []
            self.science_frames = []
            self.raw_frames = []
            self.frameLabels = {}

            #Initialise the objects to read/display
            self.masterflat     = None
            self.ff_loccentroid = None
            self.ff_psfcentroid = None
            self.ff_raw = None
            self.objid = 1

            # Read all the products
            ff = {}
            for frame in fitsFiles:
                if frame == '':
                    continue
                category = frame.category
                if category == 'MASTER_FIBER_FLAT':
                    self.frameLabels[frame.name]=frame.category+': '+frame.name
                    ff[frame.category]=frame
                    self.masterflat   = frame
                elif category == 'FF_LOCCENTROID':
                    self.frameLabels[frame.name]=frame.category+': '+frame.name
                    ff[frame.category]=frame
                    self.ff_loccentroid = frame
                elif category == 'FF_PSFCENTROID':
                    self.frameLabels[frame.name]=frame.category+': '+frame.name
                    ff[frame.category]=frame
                    self.ff_psfcentroid = frame
                elif category == 'FIBER_FLAT':
                    self.raw_frames.append(frame)
            if self.ff_psfcentroid is not None :
                self.science_frames.append(ff['FF_PSFCENTROID'])
            if self.ff_loccentroid is not None :
                self.science_frames.append(ff['FF_LOCCENTROID'])
            elif self.masterflat is not None :
                self.science_frames.append(ff['MASTER_FIBER_FLAT'])

            if self.ff_psfcentroid is not None :
                self.masterflatPlot   = PlotableFlatWithPSFcentroid(self.masterflat,self.ff_psfcentroid)
            elif self.ff_loccentroid is not None :
                self.masterflatPlot   = PlotableFlatWithLOCcentroid(self.masterflat,self.ff_loccentroid)
            elif self.masterflat is not None :
                self.masterflatPlot   = PlotableFlat(self.masterflat)

            self.science_found = len(self.science_frames) > 0
            self.raw_found = len(self.raw_frames) > 0

            self.DPR_CATG = "unknown"
            self.DPR_TYPE = "unknown"
            self.DPR_TECH = "unknown"
            if self.raw_found:
                HDUlist=pyfits.open(self.raw_frames[0].name)
                self.DPR_CATG = HDUlist[0].header['HIERARCH ESO DPR CATG']
                self.DPR_TYPE = HDUlist[0].header['HIERARCH ESO DPR TYPE']
                self.DPR_TECH = HDUlist[0].header['HIERARCH ESO DPR TECH']
                HDUlist.close()

            if self.science_found:
                self.esorex_log_file_name  = re.sub(".*/giraffe/" ,"../../", os.path.dirname(self.science_frames[0].name))+"/log_dir/esorex.log"

        def loadProducts(self, index):
            """
            Loading the plots...
            """
            super(DataPlotterManager, self).loadProducts(index)
            if self.masterflat is not None :
                if self.science_frames[index].category == 'FF_PSFCENTROID' :
                    self.masterflatPlot   = PlotableFlatWithPSFcentroid(self.masterflat,self.ff_psfcentroid)
                elif self.science_frames[index].category == 'FF_LOCCENTROID' :
                    self.masterflatPlot   = PlotableFlatWithLOCcentroid(self.masterflat,self.ff_loccentroid)
                elif self.science_frames[index].category == 'MASTER_FIBER_FLAT' :
                    self.masterflatPlot   = PlotableFlat(self.science_frames[index])
                
        # This function creates all the subplots. It is responsible for the
        # plotting layouts.
        # There can different layouts, depending on the availability of data
        # Note that subplot(I,J,K) means the Kth plot in a IxJ grid
        # Note also that the last one is actually a box with text, no graphs.
        def addSubplots(self, figure):
            self.list_subplot_image     = []
            if self.masterflat is not None :
                if self.ff_loccentroid is not None :
                    gs = matplotlib.gridspec.GridSpec(100,100)
                    self.subplot_masterflat    = figure.add_subplot(gs[0:43,0:100])
                    self.subplot_psfcentroid   = figure.add_subplot(gs[54:92,0:100])
                    self.subplot_txtinfo       = figure.add_subplot(gs[95:100,0:100])
                    #self.subplot_txtinfo       = figure.widgets.Widget(gs[95:100,0:100])
                else :
                    gs = matplotlib.gridspec.GridSpec(100,100)
                    self.subplot_masterflat    = figure.add_subplot(gs[0:72,0:100])
                    self.subplot_psfcentroid   = figure.add_subplot(gs[80:92,0:100])
                    self.subplot_txtinfo       = figure.add_subplot(gs[95:100,0:100])

                self.setupFigure(figure)
            else :
                self.subtext_nodata      = figure.add_subplot(1,1,1)

        # This is the function that makes the plots.
        # Add new plots or delete them using the given scheme.
        # The data has been already stored in self.plot_x, self.plot_xdif, etc ...
        # It is mandatory to add a tooltip variable to each subplot.
        # One might be tempted to merge addSubplots() and plotProductsGraphics().
        # There is a reason not to do it: addSubplots() is called only once at
        # startup, while plotProductsGraphics() is called always there is a
        # resize.
        def plotProductsGraphics(self):

            #if self.science_found == True and self.err_found == True:
                
            if self.masterflat is not None :

                self.plotMasterFlatPlot()
                if self.ff_loccentroid is not None :
                    if self.ff_psfcentroid is None :
                        self.showNoPSF()
                    elif not self.have_FF_PSFCENTROID :
                        self.showHavePSFbutFAILED()

                else:
                    self.showNoLOC()

                #Additional text info
                self.showTextInfo()

            else :
                #Data not found info
                self.showNoData()

        def plotMasterFlatPlot(self) :
            if type(self.masterflatPlot) is PlotableFlat :
                title_masterflat   = 'Master Flat'
                tooltip_masterflat = """Master Flat.
The master flat generated by the gimasterflat recipe, apparently processing failed, probably the recipe was
unable to find the expected number of fibres."""
                self.masterflatPlot.plot(self.subplot_masterflat, title_masterflat,
                                 tooltip_masterflat)

            elif type(self.masterflatPlot) is PlotableFlatWithLOCcentroid :
                title_masterflat   = 'Master Flat with LOCCENTROID'
                tooltip_masterflat = """Master Flat.
The centroids of the fibre location overlaid on the master flat."""
                self.masterflatPlot.plot(self.subplot_masterflat, title_masterflat,
                                 tooltip_masterflat)
                title_masterflat   = 'Master Flat cross cut with LOCCENTROID'
                tooltip_masterflat = """Master Flat.
The centroids of the fibre location overlaid on a cross section of the master flat."""
                self.masterflatPlot.plotXsection(self.subplot_psfcentroid, title_masterflat,
                                 tooltip_masterflat)

            elif type(self.masterflatPlot) is PlotableFlatWithPSFcentroid :
                title_masterflat   = 'Master Flat with PSFCENTROID'
                tooltip_masterflat = """Master Flat.
The centroids of the PSF fitting overlaid on the master flat."""
                self.masterflatPlot.plot(self.subplot_masterflat, title_masterflat,
                                 tooltip_masterflat)
                title_masterflat   = 'Master Flat cross cut with PSFCENTROID'
                tooltip_masterflat = """Master Flat.
The centroids of the PSF fitting overlaid on a cross section of the master flat."""
                self.masterflatPlot.plotXsection(self.subplot_psfcentroid, title_masterflat,
                                 tooltip_masterflat)

        def showNoLOC(self) :
          #Data not found info
          self.subplot_psfcentroid.set_axis_off()
          self.text_nodata = 'Recipe FAILED, could not find product FF_LOCCENTROID.'
          self.subplot_psfcentroid.text(0.5, 0.5, self.text_nodata, color='red', fontsize=18,
                                   ha='center', va='center', alpha=1.0)
          self.subplot_psfcentroid.tooltip="""The gimasterflat recipe has apparently failed to even localaize the fibres.
Probably it failed to locate the expected number of fibres, try adjusting fiber-splist on the 'Fibre selection' tab, and/or the
parameters on the 'Location' tab."""

        def showNoPSF(self) :
          #Data not found info
          self.subplot_psfcentroid.set_axis_off()
          self.text_nodata = 'Recipe FAILED, found FF_LOCCENTROID product,\nbut could not find product FF_PSFCENTROID.'
          self.subplot_psfcentroid.text(0., 0., self.text_nodata, color='red', fontsize=16,
                                   ha='left', va='top', alpha=1.0)
          self.subplot_psfcentroid.tooltip="""The gimasterflat recipe has apparently failed to build the PSF models of fibres.
Possibly the localization found the right number of fibres, but failed to trace them correctly. Try adjusting the
parameters on the 'Location' and 'PSF' tab."""


        def showHavePSFbutFAILED(self) :
          #data found in working dir, but recipe still failed...
          self.subplot_psfcentroid.set_axis_off()
          self.text_nodata = 'Found FF_LOCCENTROID & FF_PSFCENTROID but recipe still FAILED\nPlease check the log file.'
          self.subplot_psfcentroid.text(0., 0., self.text_nodata, color='red', fontsize=16,
                                   ha='left', va='top', alpha=1.0)
          self.subplot_psfcentroid.tooltip="""The gimasterflat recipe has apparently failed to complete successfully.
Possibly the localization found the right number of fibres, but failed to trace them correctly. Try adjusting the
parameters on the 'Location' and 'PSF' tab."""


        def showTextInfo(self) :
            self.subplot_txtinfo.set_axis_off()
            try:
                mode = self.masterflatPlot.flat.readKeyword('HIERARCH ESO INS SLITS ID')
            except:
                mode = 'unknown'
            try:
                filter_name = self.masterflatPlot.flat.readKeyword('HIERARCH ESO INS FILT NAME')
            except:
                filter_name = 'unknown'
            try:
                grat_wlen = self.masterflatPlot.flat.readKeyword('HIERARCH ESO INS GRAT WLEN')
            except:
                grat_wlen = 'unknown'
            try:
                raw1_name = self.masterflatPlot.flat.readKeyword('HIERARCH ESO PRO REC1 RAW1 NAME')
            except:
                raw1_name = 'unknown'

            self.subplot_txtinfo.text(0.1, 0., 'DPid RAW1: %s\nDPR: %s :: %s :: %s\nMode/Setting/WLen: %s/%s/%s'
                                   %(raw1_name,
                                     self.DPR_CATG,self.DPR_TYPE,self.DPR_TECH,
                                     mode,filter_name,str(grat_wlen)
                                    ),
                                   ha='left', va='top', weight='bold')

            try:
                slit = self.sci_extracted.extractedscience.readKeyword('HIERARCH ESO INS SLIT NAME')
                self.subplot_txtinfo.text(0.5, 0., 'Slit name (LSS): '+slit, 
                                   ha='left', va='center', weight='bold')
            except:
                pass
            try:
                checksum = self.sci_extracted.extractedscience.readKeyword('HIERARCH ESO INS MOS CHECKSUM')
                self.subplot_txtinfo.text(0.5, 0., 'Slits checksum (MOS): '+str(checksum), 
                                   ha='left', va='center', weight='bold')
            except:
                pass
            try:
                mask_id = self.sci_extracted.extractedscience.readKeyword('HIERARCH ESO INS MASK ID')
                self.subplot_txtinfo.text(0.5, 0., 'Mask id (MXU): '+mask_id, 
                                   ha='left', va='center', weight='bold')
            except:
                pass
            try:
                target_name = self.sci_extracted.extractedscience.readKeyword('HIERARCH ESO OBS TARG NAME')
                self.subplot_txtinfo.text(0.1, 0.6, 'Target name: '+target_name, 
                                   ha='left', va='center', weight='bold')
            except:
                pass

        def showNoData(self) :
            #Data not found info
            self.subtext_nodata.set_axis_off()
            self.text_nodata = 'Master Flat data not found in the products\n(PRO.CATG=MASTER_FIBER_FLAT)'
            self.subtext_nodata.text(0.1, 0.6, self.text_nodata, color='#11557c', fontsize=18,
                                   ha='left', va='center', alpha=1.0)
            self.subtext_nodata.tooltip='Master Flat data not found in the products'


        def plotWidgets(self) :
            widgets = list()
            if False and self.masterflat is not None :
                # Clickable subplot
                self.clickablesmapped = InteractiveClickableSubplot(
                       self.subplot_masterflat, self.setExtractedObject)
                widgets.append(self.clickablesmapped)
            return widgets

        def setExtractedObject(self, point) :
            obj = self.masterflat.getObjectInPosition(point.ydata)
            if obj != -1 :
                self.sci_extracted.selectObject(obj)
            self.plotSciExtracted()

    
        # This function specifies which are the parameters that should be presented
        # in the window to be edited.
        # Note that the parameter has to be also in the in_sop port (otherwise it
        # won't appear in the window)
        # The descriptions are used to show a tooltip. They should match one to one
        # with the parameter list
        # Note also that parameters have to be prefixed by the 'recipe name:'
        def setInteractiveParameters(self):
            paramList = list()
            paramList.append(reflex.RecipeParameter('gimasterflat','fiber-splist',group='Geom',description='Index list of spectra to use for localization (e.g. 2,10,30-40,55)'))
            paramList.append(reflex.RecipeParameter('gimasterflat','bsremove-method',group='Bkg Sub',description='Backgruond subtraction method'))
            paramList.append(reflex.RecipeParameter('gimasterflat','bsremove-yorder',group='Bkg Sub',description='Background subtraction yorder of 2-D polynomial fit'))
            paramList.append(reflex.RecipeParameter('gimasterflat','sloc-start',group='LOC',description='Bin along x-axis. [-1]'))
            paramList.append(reflex.RecipeParameter('gimasterflat','sloc-binsize',group='LOC',description='Initial localization detection xbin size. [-1]'))
            paramList.append(reflex.RecipeParameter('gimasterflat','sloc-retries',group='LOC',description='Initial localization detection xbin retries. [10]'))
            paramList.append(reflex.RecipeParameter('gimasterflat','sloc-noise',group='LOC',description='Threshold multiplier. [7.0]'))
            paramList.append(reflex.RecipeParameter('gimasterflat','psf-binsize',group='PSF',description='Size of bin along dispersion axis. [64]'))
            return paramList

        def setWindowHelp(self):
            help_text = """
In this window, the user will interact with the GIRAF flat-field reduction
"""
            return help_text

        def setWindowTitle(self):
            title = 'GIRAF Interactive masterflat reduction'
            return title

except ImportError:
    DataPlotterManager = None


if __name__ == '__main__':
    runInteractiveApp(DataPlotterManager)
