# import the needed modules
try:
  import reflex
  import matplotlib.gridspec
  import reflex_plot_widgets
  import os
  import_sucess = True

#NOTE for developers: 
# -If you want to modify the current script to cope
#  with different parameters, this is the function to modify:
#  setInteractiveParameters()
# -If you want to modify the current script to read different data from
#  the input FITS, this is the function to modify:
#  readFitsData()                  (from class DataPlotterManager) 
# -If you want to modify the current script to modify the plots (using the same
#  data),  this is the function to modify:
#  plotProductsGraphics()          (from class DataPlotterManager)
# -If you want to modify the text that appears in the "Help" button,
#  this is the function to modify:
#  setWindowHelp()
# -If you want to modify the title of the window, modify this function:
#  setWindowTitle()


  #This class deals with the specific details of data reading and final plotting.
  class DataPlotterManager:
    # 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().
    # Add/delete these variables as you need (only that plotProductsGraphics()
    # has to use the same names).
    # You can also create some additional variables (like statistics) after
    # reading the files.
    # If you use a control variable (self.xxx_found), you can modify 
    # later on the layout of the plotting window based on the presence of 
    # given input files. 
    # sof contains all the set of frames
    def __init__(self):
        #self.xrc_file=os.path.join(os.path.dirname(__file__),'giraf_reflex_interactive_gui.xrc')
        self.xrc_file=None
    
    def readFitsData(self, fitsFiles):
      import os.path, glob
      for F in glob.glob('../../gimasterflat_1/latest/*.fits') :
          HDUlist=pyfits.open(F)
          fitsFiles.append(reflex.FitsFile(os.path.abspath(F), HDUlist[0].header['HIERARCH ESO PRO CATG'], None, None))
          HDUlist.close()

      #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
      frames = dict()
      for frame in fitsFiles :
        if frame == '' :
          continue
        category = frame.category
        frames[category] = frame

      # re-define plotting functions to enable callbacks
      self._add_subplots = self._add_subplots
      self._plot = self._data_plot

      #for inst_mode in ('MEDUSA', 'IFU', 'ARGUS') :
      self.radio_button_opts = {}
      if 'MASTER_FIBER_FLAT' in frames:
          self.masterflat   = PlotableFlat(frames["MASTER_FIBER_FLAT"])
          self.radio_button_opts = {'Master FLAT':0}
          if 'FF_LOCCENTROID' in frames :
              self.ff_loccentroid = frames['FF_LOCCENTROID']
              self.masterflatWithLOC   = PlotableFlatWithLOCcentroid(frames["MASTER_FIBER_FLAT"],self.ff_loccentroid)
              self.radio_button_opts = {'Localization':0}
              if 'FF_PSFCENTROID' in frames :
                  self.ff_psfcentroid = frames['FF_PSFCENTROID']
                  self.masterflatWithPSF   = PlotableFlatWithPSFcentroid(frames["MASTER_FIBER_FLAT"],self.ff_psfcentroid)
                  self.radio_button_opts = {'Localization':0,'PSF':1}
          if 'FIBER_FLAT' in frames:
              self.ff_raw = frames['FIBER_FLAT']
              HDUlist=pyfits.open(self.ff_raw.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()

          # Set the initial radio button selections (value 0)
          self.radio_button_label = [key for key, value in self.radio_button_opts.iteritems() if value == 0][0]
      else:
          # Set the plotting functions to NODATA ones
          self._add_subplots = self._add_nodata_subplots
          self._plot = self._nodata_plot


    # 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._add_subplots(figure)

    def _add_subplots(self, figure):
      if self.masterflat is not None :
        if self.ff_loccentroid is not None :
            gs = matplotlib.gridspec.GridSpec(100,100)
            self.axradiobutton         = figure.add_subplot(gs[0:5,0:20])
            self.subplot_masterflat    = figure.add_subplot(gs[7:45,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])

    def _add_nodata_subplots(self, figure):
        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):
        self._plot()

    def _data_plot(self):
      if self.masterflat is not None :

        #MasterFlat + ff_loccentroid (if existing...)
        if self.ff_loccentroid is not None :
            self.plotMasterFlatWithLOCcentroid()
            #MasterFlat + ff_psfcentroid (if existing...)
            if self.ff_psfcentroid is not None :
                if (self.radio_button_opts[self.radio_button_label] == 0) :
                    self.plotMasterFlatWithLOCcentroid()
                elif (self.radio_button_opts[self.radio_button_label] == 1) :
                    self.plotMasterFlatWithPSFcentroid()
            else:
                self.showNoPSF()
        else:
            self.plotMasterFlat()
            self.showNoLOC()

        #Additional text info
        self.showTextInfo()

    def _nodata_plot(self):
        #else :
        #Data not found info
        self.showNoData()
  
    def plotMasterFlat(self) :
        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 epected number of fibres."""
        self.masterflat.plot(self.subplot_masterflat, title_masterflat,
                             tooltip_masterflat)

    def plotMasterFlatWithLOCcentroid(self) :
        title_masterflat   = 'Master Flat with LOCCENTROID'
        tooltip_masterflat = """Master Flat.
The centroids of the fibre location overlaid on the master flat."""
        self.masterflatWithLOC.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.masterflatWithLOC.plotXsection(self.subplot_psfcentroid, title_masterflat,
                             tooltip_masterflat)

    def plotMasterFlatWithPSFcentroid(self) :
        title_masterflat   = 'Master Flat with PSFCENTROID'
        tooltip_masterflat = """Master Flat.
The centroids of the PSF fitting overlaid on the master flat."""
        self.masterflatWithPSF.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.masterflatWithPSF.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.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 buid 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 showTextInfo(self) :
      self.subplot_txtinfo.set_axis_off()
      try:
        mode = self.masterflat.flat.readKeyword('HIERARCH ESO INS SLITS ID')
      except:
        mode = 'unknown'
      try:
        filter_name = self.masterflat.flat.readKeyword('HIERARCH ESO INS FILT NAME')
      except:
        filter_name = 'unknown'
      try:
        grat_wlen = self.masterflat.flat.readKeyword('HIERARCH ESO INS GRAT WLEN')
      except:
        grat_wlen = 'unknown'
      try:
        raw1_name = self.masterflat.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 (PRO.CATG=)'
      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)
        
      if self.masterflat is not None :
          if self.ff_loccentroid is not None :
              # Radio button
              self.radiobutton = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton, 
                                                                     self.setRadioCallback, 
                                                                     [key for key,value in sorted(self.radio_button_opts.items(),key= lambda k: k[1])],
                                                                     self.radio_button_opts.get(self.radio_button_label),
                                                                     title='Select item to display')
              widgets.append(self.radiobutton)

      return widgets

    def setRadioCallback(self, label) :

        # Only do something if user changes the button
        if (label != self.radio_button_label):
            self.radio_button_label = label
            self._plot()

    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:
  import_sucess = 'false'
  print "Error importing modules pyfits, wx, matplotlib, numpy"

#This is the 'main' function
if __name__ == '__main__':

  # import reflex modules
  import reflex_interactive_app
  import sys

  # import GIRAF reflex modules
  from giraf_plot_common import *

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

  #Check if import failed or not
  if import_sucess == 'false' :
    interactive_app.setEnableGUI('false')

  #Open the interactive window if enabled
  if interactive_app.isGUIEnabled() :
    #Get the specific functions for this window
    dataPlotManager = DataPlotterManager()
    interactive_app.setPlotManager(dataPlotManager)
    interactive_app.showGUI()
  else :
    interactive_app.passProductsThrough()

  # print outputs
  interactive_app.print_outputs()

  sys.exit()
