# ========================================================================================================
#
# John Pritchard
#
# ========================================================================================================
try:
  import numpy
  from pipeline_display import *
  from pipeline_product import *
  from reflex_plot_widgets import *
  from numpy.polynomial import Polynomial
  numpy.seterr(invalid='ignore')
  import giraf_der_snr
  from giSpectrumDisplay import *
  #from pipeline_display import SpectrumDisplay as giSpectrumDisplay
  
  from astropy.coordinates import SkyCoord
  from astropy.table import Table
  #from astroquery.utils import TableList
  import astropy.units as u
except ImportError:
  donothing=1

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

## Potentially interesting code here...
## http://python4esac.github.io/plotting/specnorm.html

## These are the GIRAFFE routines, hacked from the FORS ones,
## see below for the remaining FORS ones as examples...
class PlotableFlat(object) :
  def __init__(self, fits_flat):
    self.flat      = PipelineProduct(fits_flat)
    self.flatdisp  = ImageDisplay()
    self.loadFromFits()

  def loadFromFits(self) :
    #Reading the flat image
    self.flat.readImage()

  def plot(self, subplot, title, tooltip):
    self.flatdisp.setLabels('X [pix]', 'Y [pix]')
    self.flatdisp.display(subplot, title, tooltip, self.flat.image)

################################################################################

class PlotableFlatWithLOCcentroid(object) :
  def __init__(self, fits_flat, fits_ff_loccentroid):
    self.Xsectplot = -1
    self.flat      = PipelineProduct(fits_flat)
    self.ff_loccentroid = None
    if fits_ff_loccentroid is not None:
      self.ff_loccentroid = PipelineProduct(fits_ff_loccentroid)
    self.flatdisp  = ImageDisplay()
    self.Xsectplot = giSpectrumDisplay()
    self.loadFromFits()

    self.NAXIS1=self.flat.all_hdu[0].header['NAXIS1']
    self.NAXIS2=self.flat.all_hdu[0].header['NAXIS2']
    # Params are available via the interactive_app.getCurrentParameterValue()
    # but I can't figure out how to access them from here... so I need to
    # make my own param dictionary...
    i=0
    pFound=True
    self.params = {}
    self.params[self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d NAME' %(i+1)]] = self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d VALUE' %(i+1)]
    while pFound :
        try :
            self.params[self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d NAME' %(i+1)]] = self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d VALUE' %(i+1)]
            i+=1
        except :
            pFound=False
    self.sloc_start=int(self.params['sloc-start'])
    self.setXsectRow(self.sloc_start)

  def loadFromFits(self) :
    #Reading the flat image
    self.flat.readImage()
    if self.ff_loccentroid is not None:
      self.ff_loccentroid.readImage()
      self.fibres = self.ff_loccentroid.image.shape[1]

      #Creating the points to plot based on the polynomail traces
      self.ypos_fibres = list(range(self.ff_loccentroid.image.shape[0]))
      self.xpos_fibres = []
      for fibre in numpy.arange(self.fibres) :
        self.xpos_fibres.append(self.ff_loccentroid.all_hdu[0].data[:,fibre]+1.)


  def plot(self, subplot, title, tooltip):
    subplot.cla()
    self.flatdisp.setLabels('X [pix]', 'Y [pix]')
    self.flatdisp.display(subplot, title, tooltip, self.flat.image)
    lss=self.sloc_start
    if lss == -1 :
        lss=int(self.NAXIS2/2)
    if self.ff_loccentroid is not None:
      subplot.autoscale(enable=False)
      ## EVEN number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN']%2 == 0)] :
        subplot.plot(self.xpos_fibres[fibre], self.ypos_fibres,
                     linestyle='solid',color='red')
      ## ODD number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN']%2 == 1)] :
        subplot.plot(self.xpos_fibres[fibre], self.ypos_fibres,
                     linestyle='solid',color='yellow')
      ## Label the subslits...
      for SS in numpy.unique(self.ff_loccentroid.all_hdu[1].data['SSN']):
          subplot.annotate(str(SS), xy=(numpy.mean(numpy.array(self.xpos_fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN'] == SS)][:,lss]),self.NAXIS2),
              xycoords='data', horizontalalignment='center', verticalalignment='top')
      ## Label the fibres...
      j=0
      for i in numpy.unique(self.ff_loccentroid.all_hdu[1].data['FPS']):
        subplot.annotate(str(i), xy=(numpy.array(self.xpos_fibres)[j,lss],lss),
              xycoords='data', horizontalalignment='center', verticalalignment='bottom', rotation=90.)
        j+=1
    ## A horizontal line at the sloc-start value...
    subplot.plot([0,self.NAXIS1], [lss,lss],
                 linestyle='solid', color='black', linewidth=2)

  def setXsectRow(self, row):
      self.XsectRow=row
      if row == -1 :
          self.XsectRow=int(self.NAXIS2/2)

  def plotXsection(self, subplot, title, tooltip):
    subplot.cla()
    self.Xsectplot.setLabels('X [pix]', 'Y [ADU]')
    self.Xsectplot.display(subplot, title, tooltip, numpy.arange(self.NAXIS1)+1, self.flat.image[self.XsectRow,:])
    SSylab=subplot.get_ylim()[1]*0.99
    lss=self.sloc_start
    if lss == -1 :
        lss=int(self.NAXIS2/2)
    if self.ff_loccentroid is not None:
      subplot.autoscale(enable=False)
      ## EVEN number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN']%2 == 0)] :
        subplot.axvline(self.xpos_fibres[fibre][self.XsectRow],
                     linestyle='solid',color='red')
      ## ODD number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN']%2 == 1)] :
        subplot.axvline(self.xpos_fibres[fibre][self.XsectRow],
                     linestyle='solid',color='yellow')
      ## Label the subslits...
      for SS in numpy.unique(self.ff_loccentroid.all_hdu[1].data['SSN']):
          subplot.annotate(str(SS), xy=(numpy.mean(numpy.array(self.xpos_fibres)[numpy.where(self.ff_loccentroid.all_hdu[1].data['SSN'] == SS)][:,self.XsectRow]),SSylab),
              xycoords='data', horizontalalignment='center', verticalalignment='top')
      ## Label the fibres...
      j=0
      for i in numpy.unique(self.ff_loccentroid.all_hdu[1].data['FPS']):
        subplot.annotate(str(i), xy=(numpy.array(self.xpos_fibres)[j,self.XsectRow],0),
              xycoords='data', horizontalalignment='center', verticalalignment='bottom', rotation=90.)
        j+=1

################################################################################

class PlotableFlatWithPSFcentroid(object) :
  def __init__(self, fits_flat, fits_ff_psfcentroid):
    self.flat      = PipelineProduct(fits_flat)
    self.ff_psfcentroid = None
    if fits_ff_psfcentroid is not None:
      self.ff_psfcentroid = PipelineProduct(fits_ff_psfcentroid)
    self.flatdisp  = ImageDisplay()
    self.Xsectplot = giSpectrumDisplay()
    self.loadFromFits()

    self.NAXIS1=self.flat.all_hdu[0].header['NAXIS1']
    self.NAXIS2=self.flat.all_hdu[0].header['NAXIS2']
    # Params are available via the interactive_app.getCurrentParameterValue()
    # but I can't figure out how to access them from here... so I need to
    # make my own param dictionary...
    i=0
    pFound=True
    self.params = {}
    self.params[self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d NAME' %(i+1)]] = self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d VALUE' %(i+1)]
    while pFound :
        try :
            self.params[self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d NAME' %(i+1)]] = self.flat.all_hdu[0].header['HIERARCH ESO PRO REC1 PARAM%d VALUE' %(i+1)]
            i+=1
        except :
            pFound=False
    self.sloc_start=int(self.params['sloc-start'])
    self.setXsectRow(self.sloc_start)

  def loadFromFits(self) :
    #Reading the flat image
    self.flat.readImage()
    if self.ff_psfcentroid is not None:
      self.ff_psfcentroid.readImage()
      self.fibres = self.ff_psfcentroid.image.shape[1]

      #Creating the points to plot based on the polynomail traces
      self.ypos_fibres = list(range(self.ff_psfcentroid.image.shape[0]))
      self.xpos_fibres = []
      for fibre in range(self.fibres) :
        self.xpos_fibres.append(self.ff_psfcentroid.all_hdu[0].data[:,fibre]+1.)


  def plot(self, subplot, title, tooltip):
    subplot.cla()
    self.flatdisp.setLabels('X [pix]', 'Y [pix]')
    self.flatdisp.display(subplot, title, tooltip, self.flat.image)
    lss=self.sloc_start
    if lss == -1 :
        lss=int(self.NAXIS2/2)
    if self.ff_psfcentroid is not None:
      subplot.autoscale(enable=False)
      ## EVEN number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN']%2 == 0)] :
        subplot.plot(self.xpos_fibres[fibre], self.ypos_fibres,
                     linestyle='solid',color='blue')
      ## ODD number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN']%2 == 1)] :
        subplot.plot(self.xpos_fibres[fibre], self.ypos_fibres,
                     linestyle='solid',color='green')
      ## Label the subslits...
      for SS in numpy.unique(self.ff_psfcentroid.all_hdu[1].data['SSN']):
          subplot.annotate(str(SS), xy=(numpy.mean(numpy.array(self.xpos_fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN'] == SS)][:,lss]),self.NAXIS2),
              xycoords='data', horizontalalignment='center', verticalalignment='top')
      ## Label the fibres...
      j=0
      for i in numpy.unique(self.ff_psfcentroid.all_hdu[1].data['FPS']):
        subplot.annotate(str(i), xy=(numpy.array(self.xpos_fibres)[j,lss],lss),
              xycoords='data', horizontalalignment='center', verticalalignment='bottom', rotation=90.)
        j+=1
    ## A horizontal line at the sloc-start value...
    subplot.plot([0,self.NAXIS1], [lss,lss],
                 linestyle='solid', color='black', linewidth=2)

  def setXsectRow(self, row):
      self.XsectRow=row
      if row == -1 :
          self.XsectRow=int(self.NAXIS2/2)

  def plotXsection(self, subplot, title, tooltip):
    subplot.cla()
    self.Xsectplot.setLabels('X [pix]', 'Y [ADU]')
    self.Xsectplot.display(subplot, title, tooltip, numpy.arange(self.NAXIS1)+1, self.flat.image[self.XsectRow,:])
    SSylab=subplot.get_ylim()[1]*0.99
    lss=self.sloc_start
    if lss == -1 :
        lss=int(self.NAXIS2/2)
    if self.ff_psfcentroid is not None:
      subplot.autoscale(enable=False)
      ## EVEN number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN']%2 == 0)] :
        subplot.axvline(self.xpos_fibres[fibre][self.XsectRow],
                     linestyle='solid',color='blue')
      ## ODD number subslits...
      for fibre in numpy.arange(self.fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN']%2 == 1)] :
        subplot.axvline(self.xpos_fibres[fibre][self.XsectRow],
                     linestyle='solid',color='green')
      ## Label the subslits...
      for SS in numpy.unique(self.ff_psfcentroid.all_hdu[1].data['SSN']):
          subplot.annotate(str(SS), xy=(numpy.mean(numpy.array(self.xpos_fibres)[numpy.where(self.ff_psfcentroid.all_hdu[1].data['SSN'] == SS)][:,self.XsectRow]),SSylab),
              xycoords='data', horizontalalignment='center', verticalalignment='top')
      ## Label the fibres...
      j=0
      for i in numpy.unique(self.ff_psfcentroid.all_hdu[1].data['FPS']):
        subplot.annotate(str(i), xy=(numpy.array(self.xpos_fibres)[j,self.XsectRow],0),
              xycoords='data', horizontalalignment='center', verticalalignment='bottom', rotation=90.)
        j+=1


################################################################################

class PlotableARC_RBNSPECTRA(object) :
  def __init__(self, fits_arc_rbnspectra):
    self.rbnspectra      = PipelineProduct(fits_arc_rbnspectra)
    self.rbnspectradisp  = ImageDisplay()
    self.loadFromFits()

  def loadFromFits(self) :
    #Reading the flat image
    self.rbnspectra.readImage()

  def plot(self, subplot, title, tooltip):
    self.rbnspectradisp.setLabels('Fibre', 'Y [pix]')
    self.rbnspectradisp.display(subplot, title, tooltip, self.rbnspectra.image)



################################################################################

class PlotableSCI_RBNSPECTRA(object) :
  def __init__(self, fits_sci_rbnspectra):
    self.rbnspectra      = PipelineProduct(fits_sci_rbnspectra)
    self.rbnspectradisp  = ImageDisplay()
    self.loadFromFits()

  def loadFromFits(self) :
    #Reading the flat image
    self.rbnspectra.readImage()

  def plot(self, subplot, title, tooltip):
    self.rbnspectradisp.setLabels('Fibre', 'Y [pix]')
    self.rbnspectradisp.display(subplot, title, tooltip, self.rbnspectra.image)

  def getObjectInPosition(self, xpos) :
    return int(xpos)

################################################################################

class PlotableExtractedScience :
    def __init__(self, fits_extractedscience, fits_extractederrors, tblLists, fib_id=-1):
        self.fib_id = fib_id
        self.extractedscience  = {}
        self.extractederrors   = {}
        self.IDPs  = {}
        self.tblLists = tblLists
        self.FIBER_SETUPs = {}
        self.PLparams = {}
        for f in fits_extractedscience :
            HDUlist=pyfits.open(f.name)
            CRCM=HDUlist[0].header.get('CMETHOD','NONE')
            self.INS_MODE=HDUlist[0].header['HIERARCH ESO INS MODE']
            self.RADEC=SkyCoord(HDUlist[0].header['RA'],HDUlist[0].header['DEC'], unit=(u.deg, u.deg), frame="icrs")
            self.extractedscience[CRCM] = PipelineProduct(f)
            i=0
            plp={}
            while "HIERARCH ESO PRO REC1 PARAM%d NAME" %(i+1) in self.extractedscience[CRCM].all_hdu[0].header :
                plp[self.extractedscience[CRCM].all_hdu[0].header["HIERARCH ESO PRO REC1 PARAM%d NAME" %(i+1)]] = self.extractedscience[CRCM].all_hdu[0].header["HIERARCH ESO PRO REC1 PARAM%d VALUE" %(i+1)]
                i+=1
            self.PLparams[CRCM]=plp
            HDUlist.close()
            self.FIBER_SETUPs[CRCM]=Table.read(f.name)
            self.FIBER_SETUPs[CRCM]["RA"].unit=u.deg
            self.FIBER_SETUPs[CRCM]["DEC"].unit=u.deg
        for f in fits_extractederrors :
            HDUlist=pyfits.open(f.name)
            CRCM=HDUlist[0].header.get('CMETHOD','NONE')
            self.extractederrors[CRCM]  = PipelineProduct(f)
            HDUlist.close()
        self.spectrumdisplay   = giSpectrumDisplay()
        self.loadFromFits()

        self.sky=None
        self.skyApplyTo = None
        self.skyMethod = None
        self.skyStatistic = None
        self.skyNumFibs = None
        self.skyFibIsSelected = None
        self.skyRejected = None
        #self.computeSky("All", "All Sky Fibres", "median" )
        #self.computeSky("All", "Best <N> Fibres by S/N", "mean" )
        #self.displaySky = True
        #self.displaySignal = True
        #self.displaySignalMinusSky = True

        self.NoCRC_displaySignal = True
        self.NoCRC_displaySignalMinusSky = True
        self.NoCRC_displaySky = False
        self.NoCRC_displayErr = False
        self.AS_displaySignal = True
        self.AS_displaySignalMinusSky = True
        self.AS_displaySky = False
        self.AS_displayErr = False
        self.PC_displaySignal = True
        self.PC_displaySignalMinusSky = True
        self.PC_displaySky = False
        self.PC_displayErr = False
        self.MED_displaySignal = True
        self.MED_displaySignalMinusSky = True
        self.MED_displaySky = False
        self.MED_displayErr = False

    def loadFromFits(self) :
        self.nobj   = {}
        self.crpix  = {}
        self.crval  = {}
        self.cdelt  = {}
        self.bunit  = {}
        self.nwave  = {}
        self.isARG  = {}
        self.wave   = {}
        self.wave   = {}
        for CRCM in list(self.extractederrors.keys()) :
            self.extractederrors[CRCM].readImage()
        for CRCM in list(self.extractedscience.keys()) :
            self.extractedscience[CRCM].readImage()
            self.nobj[CRCM]   = self.extractedscience[CRCM].image.shape[1]
            self.crpix[CRCM]  = self.extractedscience[CRCM].readKeyword('CRPIX2', 0)
            self.crval[CRCM]  = self.extractedscience[CRCM].readKeyword('CRVAL2', 0)
            self.cdelt[CRCM]  = self.extractedscience[CRCM].readKeyword('CDELT2', 0)
            self.bunit[CRCM]  = self.extractedscience[CRCM].readKeyword('BUNIT', 0)
            self.nwave[CRCM]  = self.extractedscience[CRCM].image.shape[0]
            self.isARG[CRCM]  = self.extractedscience[CRCM].readKeyword('HIERARCH ESO INS MODE', 0) == 'ARG'
            self.wave[CRCM]   = (numpy.arange(1, self.nwave[CRCM]+1, 1) - self.crpix[CRCM]) * self.cdelt[CRCM] + self.crval[CRCM]
        self.setSNRarr()
        self.init_object_data()
        self.selectHighestSNR()
        self.setFluxSelected()

    def init_object_data(self, CRCM='NONE'):
        self.FIBER_SETUPs[CRCM]["SNR"]=self.SNRarr[CRCM]
        if self.isARG[CRCM] :
            self.objectList = self.FIBER_SETUPs[CRCM]['Retractor']
        else :
            self.objectList = self.FIBER_SETUPs[CRCM]['OBJECT']
        self.FPSList = self.FIBER_SETUPs[CRCM]['FPS']
        self.SSNList = self.FIBER_SETUPs[CRCM]['SSN']

        self.sciTbl = self.FIBER_SETUPs[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'] != 'S')]
        self.skyTbl = self.FIBER_SETUPs[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'] == 'S')]
        #print(self.tblLists[CRCM]['OzPoz_table'].keys())
        #print(self.tblLists[CRCM]['FLAMES FIBRE Table'].keys())
        #self.sciTbl = self.tblLists[CRCM]['OzPoz_table'][numpy.where(self.tblLists[CRCM]['OzPoz_table']['TYPE'] != 'S')]
        #print(self.sciTbl)
        #self.skyTbl = self.tblLists[CRCM]['OzPoz_table'][numpy.where(self.tblLists[CRCM]['OzPoz_table']['TYPE'] == 'S')]
        #print(self.skyTbl)
        if self.isARG[CRCM] :
            self.sciObjectList = self.sciTbl['Retractor']
        else :
            self.sciObjectList = self.sciTbl['OBJECT']
        self.skyList = self.skyTbl['OBJECT']
        self.objectSkyByName=numpy.append(self.sciObjectList[numpy.argsort(self.sciObjectList)],self.skyList[numpy.argsort(self.skyList)])

    def init_spectrumdisplay_overplot(self, subplot, CRCM='NONE', color='b') :
        op=self.spectrumdisplay.overplot(subplot, self.wave[CRCM], self.flux[CRCM], errorflux=self.err[CRCM], color = color)
        op[0].set_data([],[])
        op[1].set_verts([])
        return op
        
    def init_spec_subplot(self, subplot, CRCM='NONE') :

        wlUnit=""
        try :
            wlUnit=" ["+self.extractedscience[CRCM].all_hdu[0].header['CUNIT2']+"]"
        except :
            pass
        subplot.cla()
        self.spectrumdisplay.setLabels('Wavelength'+wlUnit, 'Total Flux ['+self.bunit[CRCM]+']')
        self.MasterDisplay=self.spectrumdisplay.display(
            subplot = subplot,
            title = "",
            tooltip = "",
            wave = self.wave[CRCM],
            flux = self.flux[CRCM],
            errorflux = self.err[CRCM],
            autolimits = True,
            color = 'b',
        )
        #self.MasterDisplay[1].set_verts(self.errVerts(self.wave[CRCM],self.flux[CRCM],self.err[CRCM]))
        self.MasterDisplay[1].set_verts([])
        self.MasterDisplay[0].set_data([],[])

        self.lineSignal=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'b')
        self.lineSignalMinusSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'g')
        self.lineSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'r')
        self.AS_lineSignal=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'b')
        self.AS_lineSignalMinusSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'g')
        self.AS_lineSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'r')
        self.PC_lineSignal=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'b')
        self.PC_lineSignalMinusSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'g')
        self.PC_lineSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'r')
        self.MED_lineSignal=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'b')
        self.MED_lineSignalMinusSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'g')
        self.MED_lineSky=self.spectrumdisplay.overplot(subplot, [], [], errorflux=[], color = 'r')
        
    def errVerts(self, wave, flux, err):
        vx=numpy.concatenate([wave,numpy.flipud(wave)])
        vy=numpy.concatenate([flux-err,numpy.flipud(flux+err)])
        return [list(zip(vx,vy))]
        

    def setCutsSubPlot(self, subplot, CRCM='NONE') :

        #self.MasterDisplay[0].set_data(self.wave[CRCM], self.flux[CRCM])
        
        #self.MasterDisplay[0].set_data([],[])
        pass

    def selectBrightest(self, CRCM='NONE'):
        if self.nobj[CRCM] == 1:
            self.fib_id = 1
        median = 0
        for obj in range(self.nobj[CRCM]) :
            new_median = numpy.median(self.extractedscience[CRCM].image[:,obj])
            if new_median > median :
                median = new_median
                self.fib_id = obj + 1

    def setSNRarr(self) :
        self.SNRarr={}
        for CRCM in list(self.extractedscience.keys()) :
            self.SNRarr[CRCM]=numpy.zeros(self.nobj[CRCM])
            if self.nobj[CRCM] == 1:
                self.fib_id = 1
            for obj in range(self.nobj[CRCM]) :
                self.SNRarr[CRCM][obj] = giraf_der_snr.DER_SNR(self.extractedscience[CRCM].image[:,obj])

    def selectHighestSNR(self, CRCM='NONE'):
        self.objectSkyBySNR=numpy.append(self.sciObjectList[numpy.flipud(numpy.argsort(self.SNRarr[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] != "S")]))],
                                         self.skyList[numpy.flipud(numpy.argsort(self.SNRarr[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")]))])
        self.objectSkyByName_ind=numpy.append(numpy.arange(self.nobj[CRCM])[numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] != "S")][numpy.argsort(self.sciObjectList)],
                                              numpy.arange(self.nobj[CRCM])[numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")][numpy.argsort(self.skyList)])
        self.objectSkyBySNR_ind=numpy.append(numpy.arange(self.nobj[CRCM])[numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] != "S")][numpy.flipud(numpy.argsort(self.SNRarr[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] != "S")]))],
                                             numpy.arange(self.nobj[CRCM])[numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")][numpy.flipud(numpy.argsort(self.SNRarr[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")]))])

        # Select Highest SNR
        if(self.fib_id == -1) :
            self.fib_id = numpy.argsort(self.SNRarr[CRCM])[-1]+1

        self.skyBySNR_ind=numpy.arange(self.nobj[CRCM])[numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")][numpy.flipud(numpy.argsort(self.SNRarr[CRCM][numpy.where(self.FIBER_SETUPs[CRCM]['TYPE'][:] == "S")]))]
        self.skyListSNR=self.SNRarr[CRCM][self.skyBySNR_ind]

    def setFluxSelected(self) :
        self.flux = {}
        self.err = {}
        self.OBJECT = {}
        self.FPS = {}
        self.SSN = {}
        self.OBJECT_SNR = {}
        for CRCM in list(self.extractedscience.keys()) :
            self.flux[CRCM] = self.extractedscience[CRCM].image[:,self.fib_id-1]
            self.err[CRCM] = self.extractederrors[CRCM].image[:,self.fib_id-1]
            self.OBJECT[CRCM] = self.FIBER_SETUPs[CRCM]['OBJECT'][self.fib_id-1]
            self.FPS[CRCM] = self.FIBER_SETUPs[CRCM]['FPS'][self.fib_id-1]
            self.SSN[CRCM] = self.FIBER_SETUPs[CRCM]['SSN'][self.fib_id-1]
            if self.isARG[CRCM] and not self.FIBER_SETUPs[CRCM]['TYPE'][self.fib_id-1] == 'S' :
                self.OBJECT[CRCM] = self.extractedscience[CRCM].all_hdu[0].header['HIERARCH ESO OBS TARG NAME']
            self.OBJECT_SNR[CRCM] = self.SNRarr[CRCM][self.fib_id-1]

    def selectObject(self, fib_id):
        self.fib_id = fib_id
        self.setFluxSelected()

    def plot(self, subplot, title, tooltip, CRCM='NONE'):

        # Plot the data...
        ltitle='Rebinned spectrum of %s, S/N = %5.2f [FPS/SSN=%d/%d, col=%d]' %(self.OBJECT[CRCM].strip(), self.OBJECT_SNR[CRCM], self.FPS[CRCM], self.SSN[CRCM], self.fib_id)
        # --------------------------------------------------------------------------------
        ## CRCM = NONE
        if self.flux is None :
            return
        if 'NONE' in list(self.flux.keys()) :
            if self.NoCRC_displaySignal :
                self.lineSignal[0].set_data(self.wave['NONE'], self.flux['NONE'])
                if self.NoCRC_displayErr :
                    self.lineSignal[1].set_verts(self.errVerts(self.wave['NONE'], self.flux['NONE'], self.err['NONE']))
                else :
                    self.lineSignal[1].set_verts([])
            else :
                self.lineSignal[0].set_data([], [])
                self.lineSignal[1].set_verts([])
            
            # Overplot the sky?
            if not self.skyApplyTo is None and not self.sky['NONE'] is None and self.NoCRC_displaySky :
                self.lineSky[0].set_data(self.wave['NONE'], self.sky['NONE'][0][:,self.fib_id-1])
                if self.NoCRC_displayErr :
                    self.lineSky[1].set_verts(self.errVerts(self.wave['NONE'], self.sky['NONE'][0][:,self.fib_id-1], self.sky['NONE'][1][:,self.fib_id-1]))
                else :
                    self.lineSky[1].set_verts([])
            else :
                self.lineSky[0].set_data([],[])
                self.lineSky[1].set_verts([])
            # Overplot the spectrum-sky?
            if not self.skyApplyTo is None and not self.sky['NONE'] is None and self.NoCRC_displaySignalMinusSky :
                self.lineSignalMinusSky[0].set_data(self.wave['NONE'], self.flux['NONE']-self.sky['NONE'][0][:,self.fib_id-1])
                if self.NoCRC_displayErr :
                    self.lineSignalMinusSky[1].set_verts(self.errVerts(self.wave['NONE'], self.flux['NONE']-self.sky['NONE'][0][:,self.fib_id-1], self.err['NONE']+self.sky['NONE'][1][:,self.fib_id-1]))
                else :
                    self.lineSignalMinusSky[1].set_verts([])
            else :
                self.lineSignalMinusSky[0].set_data([],[])
                self.lineSignalMinusSky[1].set_verts([])
        # --------------------------------------------------------------------------------
        ## CRCM = astroSCRAPPY
        if 'ASTROSCRAPPY' in list(self.flux.keys()) :
            if self.AS_displaySignal :
                self.AS_lineSignal[0].set_data(self.wave['ASTROSCRAPPY'], self.flux['ASTROSCRAPPY'])
                if self.AS_displayErr :
                    self.AS_lineSignal[1].set_verts(self.errVerts(self.wave['ASTROSCRAPPY'], self.flux['ASTROSCRAPPY'], self.err['ASTROSCRAPPY']))
                else :
                    self.AS_lineSignal[1].set_verts([])
            else :
                self.AS_lineSignal[0].set_data([], [])
                self.AS_lineSignal[1].set_verts([])
            
            # Overplot the sky?
            if not self.skyApplyTo is None and not self.sky['ASTROSCRAPPY'] is None and self.AS_displaySky :
                self.AS_lineSky[0].set_data(self.wave['ASTROSCRAPPY'], self.sky['ASTROSCRAPPY'][0][:,self.fib_id-1])
                if self.AS_displayErr :
                    self.AS_lineSky[1].set_verts(self.errVerts(self.wave['ASTROSCRAPPY'], self.sky['ASTROSCRAPPY'][0][:,self.fib_id-1], self.sky['ASTROSCRAPPY'][1][:,self.fib_id-1]))
                else :
                    self.AS_lineSky[1].set_verts([])
            else :
                self.AS_lineSky[0].set_data([],[])
                self.AS_lineSky[1].set_verts([])
            # Overplot the spectrum-sky?
            if not self.skyApplyTo is None and not self.sky['ASTROSCRAPPY'] is None and self.AS_displaySignalMinusSky :
                self.AS_lineSignalMinusSky[0].set_data(self.wave['ASTROSCRAPPY'], self.flux['ASTROSCRAPPY']-self.sky['ASTROSCRAPPY'][0][:,self.fib_id-1])
                if self.AS_displayErr :
                    self.AS_lineSignalMinusSky[1].set_verts(self.errVerts(self.wave['ASTROSCRAPPY'], self.flux['ASTROSCRAPPY']-self.sky['ASTROSCRAPPY'][0][:,self.fib_id-1], self.err['ASTROSCRAPPY']+self.sky['ASTROSCRAPPY'][1][:,self.fib_id-1]))
                else :
                    self.AS_lineSignalMinusSky[1].set_verts([])
            else :
                self.AS_lineSignalMinusSky[0].set_data([],[])
                self.AS_lineSignalMinusSky[1].set_verts([])
        # --------------------------------------------------------------------------------
        ## CRCM = PyCosmic
        if 'PYCOSMIC' in list(self.flux.keys()) :
            if self.PC_displaySignal :
                self.PC_lineSignal[0].set_data(self.wave['PYCOSMIC'], self.flux['PYCOSMIC'])
                if self.PC_displayErr :
                    self.PC_lineSignal[1].set_verts(self.errVerts(self.wave['PYCOSMIC'], self.flux['PYCOSMIC'], self.err['PYCOSMIC']))
                else :
                    self.PC_lineSignal[1].set_verts([])
            else :
                self.PC_lineSignal[0].set_data([], [])
                self.PC_lineSignal[1].set_verts([])
            
            # Overplot the sky?
            if not self.skyApplyTo is None and not self.sky['PYCOSMIC'] is None and self.PC_displaySky :
                self.PC_lineSky[0].set_data(self.wave['PYCOSMIC'], self.sky['PYCOSMIC'][0][:,self.fib_id-1])
                if self.PC_displayErr :
                    self.PC_lineSky[1].set_verts(self.errVerts(self.wave['PYCOSMIC'], self.sky['PYCOSMIC'][0][:,self.fib_id-1], self.sky['PYCOSMIC'][1][:,self.fib_id-1]))
                else :
                    self.PC_lineSky[1].set_verts([])
            else :
                self.PC_lineSky[0].set_data([],[])
                self.PC_lineSky[1].set_verts([])
            # Overplot the spectrum-sky?
            if not self.skyApplyTo is None and not self.sky['PYCOSMIC'] is None and self.PC_displaySignalMinusSky :
                self.PC_lineSignalMinusSky[0].set_data(self.wave['PYCOSMIC'], self.flux['PYCOSMIC']-self.sky['PYCOSMIC'][0][:,self.fib_id-1])
                if self.PC_displayErr :
                    self.PC_lineSignalMinusSky[1].set_verts(self.errVerts(self.wave['PYCOSMIC'], self.flux['PYCOSMIC']-self.sky['PYCOSMIC'][0][:,self.fib_id-1], self.err['PYCOSMIC']+self.sky['PYCOSMIC'][1][:,self.fib_id-1]))
                else :
                    self.PC_lineSignalMinusSky[1].set_verts([])
            else :
                self.PC_lineSignalMinusSky[0].set_data([],[])
                self.PC_lineSignalMinusSky[1].set_verts([])
        ## CRCM = Median
        if 'MEDIAN' in list(self.flux.keys()) :
            if self.MED_displaySignal :
                self.MED_lineSignal[0].set_data(self.wave['MEDIAN'], self.flux['MEDIAN'])
                if self.MED_displayErr :
                    self.MED_lineSignal[1].set_verts(self.errVerts(self.wave['MEDIAN'], self.flux['MEDIAN'], self.err['MEDIAN']))
                else :
                    self.MED_lineSignal[1].set_verts([])
            else :
                self.MED_lineSignal[0].set_data([], [])
                self.MED_lineSignal[1].set_verts([])
            
            # Overplot the sky?
            if not self.skyApplyTo is None and not self.sky['MEDIAN'] is None and self.MED_displaySky :
                self.MED_lineSky[0].set_data(self.wave['MEDIAN'], self.sky['MEDIAN'][0][:,self.fib_id-1])
                if self.MED_displayErr :
                    self.MED_lineSky[1].set_verts(self.errVerts(self.wave['MEDIAN'], self.sky['MEDIAN'][0][:,self.fib_id-1], self.sky['MEDIAN'][1][:,self.fib_id-1]))
                else :
                    self.MED_lineSky[1].set_verts([])
            else :
                self.MED_lineSky[0].set_data([],[])
                self.MED_lineSky[1].set_verts([])
            # Overplot the spectrum-sky?
            if not self.skyApplyTo is None and not self.sky['MEDIAN'] is None and self.MED_displaySignalMinusSky :
                self.MED_lineSignalMinusSky[0].set_data(self.wave['MEDIAN'], self.flux['MEDIAN']-self.sky['MEDIAN'][0][:,self.fib_id-1])
                if self.MED_displayErr :
                    self.MED_lineSignalMinusSky[1].set_verts(self.errVerts(self.wave['MEDIAN'], self.flux['MEDIAN']-self.sky['MEDIAN'][0][:,self.fib_id-1], self.err['MEDIAN']+self.sky['MEDIAN'][1][:,self.fib_id-1]))
                else :
                    self.MED_lineSignalMinusSky[1].set_verts([])
            else :
                self.MED_lineSignalMinusSky[0].set_data([],[])
                self.MED_lineSignalMinusSky[1].set_verts([])
        self.MasterDisplay[2].set_text(ltitle)


    def computeSky(self, applyTo, method, statistic, numFibs=None ):
        self.sky={}
        for CRCM in list(self.extractedscience.keys()) :
            self.sky[CRCM] = self.computeSkyCRCM(applyTo, method, statistic, numFibs=numFibs, CRCM=CRCM)
        return self.sky

    def copyCRCkwsToHeader(self, HDU, CRCM="NONE"):
        ## create list of keywords with:
        # grep 'header.append(.*' giraf_astroscrappy_multi.py giraf_wfr_reflex_{median,pycosmic}_multi.py | gsed -e 's/.*header.append((\(.*\),.*/\1/' -e "s/'/:/g" -e 's/:/"/g' -e 's/^/          /' -e 's/,.*//' -e 's/$/,/'| sort -u
        for K in [
          "CMETHOD",
          "CRBPM",
          "CRGAIN",
          "CRGFWHM",
          "CRITER",
          "CRMBIAS",
          "CRRADIUS",
          "CRRBOX",
          "CRRLIM",
          "CRRON",
          "CRSIGLIM",
          "HIERARCH ESO DRS ASCRAP BIAS NAME",
          "HIERARCH ESO DRS ASCRAP BIASSUB",
          "HIERARCH ESO DRS ASCRAP GAIN",
          "HIERARCH ESO DRS ASCRAP RON",
          "HIERARCH ESO DRS ASCRAP",
          "HIERARCH ESO DRS CMETHOD",
          "HIERARCH ESO DRS PYCOSMIC BIAS NAME",
          "HIERARCH ESO DRS PYCOSMIC BIASSUB",
          "HIERARCH ESO DRS PYCOSMIC GAIN",
          "HIERARCH ESO DRS PYCOSMIC RON",
          "HIERARCH ESO DRS PYCOSMIC",
          "NFILES",        
          ] :
              if K in self.extractedscience[CRCM].all_hdu[0].header :
                  HDU.header[K]=self.extractedscience[CRCM].all_hdu[0].header[K]
                  if K == "NFILES" :
                      for i in range(self.extractedscience[CRCM].all_hdu[0].header[K]) :
                          if "IFILES"+str(i) in self.extractedscience[CRCM].all_hdu[0].header :
                              HDU.header["IFILES"+str(i)]=self.extractedscience[CRCM].all_hdu[0].header["IFILES"+str(i)]
                              

    def updateSkyHeader(self, HDU, CRCM="NONE"):
        HDU.header["HIERARCH ESO DRS SKY APPLYTO"]=self.skyApplyTo
        HDU.header["HIERARCH ESO DRS SKY METHOD"]=self.skyMethod
        HDU.header["HIERARCH ESO DRS SKY STATISTIC"]=self.skyStatistic
        HDU.header["HIERARCH ESO DRS SKY NUMFIBS"]=self.skyNumFibs
        HDU.header["HIERARCH ESO DRS SKY REJECT"]=numpy.any(self.skyRejected == False)
        j=0
        for i in range(len(self.FIBER_SETUPs[CRCM][self.skyBySNR_ind])) :
            HDU.header["HIERARCH ESO DRS SKY IDX%03d ID" %(self.skyBySNR_ind[i])]=self.skyList[i]
            HDU.header["HIERARCH ESO DRS SKY IDX%03d SNR" %(self.skyBySNR_ind[i])]=self.skyListSNR[i]
            HDU.header["HIERARCH ESO DRS SKY IDX%03d ISSEL" %(self.skyBySNR_ind[i])]=self.skyFibIsSelected[i]
            if self.skyBySNR_ind[i] in self.skyBySNR_ind[self.skyFibIsSelected] :
                HDU.header["HIERARCH ESO DRS SKY IDX%03d ISREJ" %(self.skyBySNR_ind[i])]=not self.skyRejected[j]
                j+=1
            else :
                HDU.header["HIERARCH ESO DRS SKY IDX%03d ISREJ" %(self.skyBySNR_ind[i])]=True

    def computeSkyCRCM(self, applyTo, method, statistic, numFibs=None, CRCM='NONE' ):
        sky=None
        self.skyApplyTo = applyTo
        self.skyMethod = method
        self.skyStatistic = statistic
        self.skyNumFibs = numFibs

        if applyTo == "None" :
            return sky

        cs_OneSkyValue=False
        if method == "None" :
            return sky
        elif method == "All Sky Fibres" :
            cs_skyfibs = self.skyBySNR_ind[self.skyFibIsSelected][self.skyRejected]
            cs_fibrange = [self.fib_id-1,]
            cs_OneSkyValue=True
        elif method == "Best <N> Fibres by S/N" :
            cs_skyfibs = self.skyBySNR_ind[self.skyFibIsSelected][self.skyRejected][0:numFibs]
            cs_fibrange = [self.fib_id-1,]
            cs_OneSkyValue=True
        elif method == "Nearest <N> Fibres" :
            if numFibs == len(self.skyList) :
                cs_skyfibs = self.skyBySNR_ind[self.skyFibIsSelected][self.skyRejected]
                cs_fibrange = [self.fib_id-1,]
                cs_OneSkyValue=True
            else :
                cs_fibrange = list(range(self.nobj[CRCM]))
                if self.isARG[CRCM] :
                    tc=Table([[self.RADEC.ra.deg,],[self.RADEC.dec.deg,]], names=("RA","DEC"))
                else:
                    tc=self.FIBER_SETUPs[CRCM]
                otList = searchInCat(tc, self.FIBER_SETUPs[CRCM][self.skyBySNR_ind][self.skyFibIsSelected][self.skyRejected], rad=[0.,30.*60.], maxsources=numFibs)
        elif method == "Improves S/N" :
            print(("method = %s Not yet implemented" %(method)))
        else :
            print(("Error, method = %s not supported" %(method)))
            return sky

        if applyTo == "All" :
            # Apply to all science spectra
            pass
        elif applyTo == "One" :
            print(("applyTo = %s Not yet implemented" %(applyTo)))
            # Apply to only the selected science spectra
        else :
            print(("Error, appyTo = %s not supported" %(appyTo)))
            return sky

        if not self.sky is None and CRCM in list(self.sky.keys()) :
            sky=self.sky[CRCM]
        else :
            sky=numpy.array([numpy.zeros(numpy.shape(self.extractedscience[CRCM].image)),numpy.zeros(numpy.shape(self.extractederrors[CRCM].image)),])

        for fib in cs_fibrange :
            if method == "Nearest <N> Fibres" :
                if numFibs != len(self.skyList) :
                    if fib+1 in self.skyTbl["INDEX"] :
                        # For a sky fibre the closest sky fib will always be itself, so exclude that one...
                        cs_skyfibs=otList["skys"][numpy.where(otList["skys"]["_idx"] == fib)]["INDEX"][1:numFibs+1]-1
                    else :
                        cs_skyfibs=otList["skys"][numpy.where(otList["skys"]["_idx"] == fib)]["INDEX"][0:numFibs]-1
            if statistic == "None" :
                sky=None
                return sky
            elif statistic.lower() == "median" :
                cs_sky=numpy.array([
                  numpy.median(self.extractedscience[CRCM].image[:,cs_skyfibs],1),
                  numpy.mean(self.extractederrors[CRCM].image[:,cs_skyfibs],1),
                ])
            elif statistic.lower() == "mean" :
                cs_sky=numpy.array([
                  numpy.mean(self.extractedscience[CRCM].image[:,cs_skyfibs],1),
                  numpy.mean(self.extractederrors[CRCM].image[:,cs_skyfibs],1),
                ])
            elif statistic.lower() == "mode" :
                print(("statistic = %s Not yet implemented" %(statistic)))
                ## If ever to implement, probably best to use scipy.statsi.mode(),see http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.mode.html#scipy.stats.mode
                ## and https://stackoverflow.com/questions/16330831/most-efficient-way-to-find-mode-in-numpy-array
            else :
                print(("Error, statistic = %s not supported" %(statistic)))
                sky=None
                return sky

            if applyTo == "All" and cs_OneSkyValue :
                # Apply to all science spectra
                sky=numpy.array([
                  numpy.transpose(cs_sky[0]*numpy.transpose(numpy.ones(numpy.shape(self.extractedscience[CRCM].image)))),
                  numpy.transpose(cs_sky[1]*numpy.transpose(numpy.ones(numpy.shape(self.extractederrors[CRCM].image)))),
                ])
            elif applyTo == "One" :
                #print("applyTo = %s Not yet implemented" %(applyTo))
                # Apply to only the selected science spectra
                sky[0][self.fib_id-1]=cs_sky[0]
                sky[1][self.fib_id-1]=cs_sky[1]
            elif not cs_OneSkyValue :
                sky[0][:,fib-1]=cs_sky[0]
                sky[1][:,fib-1]=cs_sky[1]
#        else :
#            print("Error, appyTo = %s not supported" %(applyTo))
#            sky=None
#            return sky

        #print(numpy.mean(sky[0][:,self.fib_id-1]))
        #print(numpy.mean(sky[1][:,self.fib_id-1]))
        return sky
################################################################################
def searchInCat(tc, catTable, rad, _maxMag=None, _magCol=None, _frame="icrs", maxsources=10000 ):

    #catroot, catroot, defMagCol, dict_magCol, colNames = vizierVars(catName)

    if _maxMag != None :
        maxMag=numpy.array(_maxMag.split(','), dtype='|S30').astype(numpy.float)
    else :
        maxMag=numpy.array([99.,])
    magCol = _magCol

    if magCol == None :
        magCol = 'Mag'

    maxSources=numpy.array(maxsources).astype(numpy.float)

    # How do I get the frame of the catalog...???
    catalog = SkyCoord(catTable["RA"],catTable["DEC"])

    if numpy.isscalar(tc["RA"]) :
        c=SkyCoord([tc["RA"],],[tc["DEC"],], unit=(u.deg, u.deg), frame=_frame)
    else :
        c=SkyCoord(tc["RA"],tc["DEC"], unit=(u.deg, u.deg), frame=_frame)

    if len(tc) > 0 :
        idxc, idxcatalog, d2d, d3d = catalog.search_around_sky(c, rad[1]*u.arcsec)
        idx=numpy.where(d2d > rad[0]*u.arcsec)
        sidxc=idxc[idx]
        sidxcatalog=idxcatalog[idx]
        st=catTable[sidxcatalog]
        sd2d=d2d[idx]
        if _maxMag != None :
            idx=numpy.where(st[dict_magCol[magCol]] < maxMag[0])
            st=st[idx]
            sidxc=sidxc[idx]
            sidxcatalog=sidxcatalog[idx]
            sd2d=sd2d[idx]
            if len(maxMag) > 1 :
                idx=numpy.where(st[dict_magCol[magCol]] > maxMag[1])
                st=st[idx]
                sidxc=sidxc[idx]
                sidxcatalog=sidxcatalog[idx]
                sd2d=sd2d[idx]
        #st.sort("_r")
        # Matched objects in input Coordinates
        mc=c[numpy.unique(sidxc)]
        # UnMatched objects in input Coordinates
        umcidx = [x for x in range(len(c)) if x not in sidxc]
        umc=c[umcidx]

        st['_idx']=sidxc
        st['_r']=sd2d.deg
        st['_r'].unit=u.deg
        tmc=Table()
        tmc['RA']=mc.ra.deg
        tmc['DEC']=mc.dec.deg
        tmc['_idx']=numpy.unique(sidxc)
        tumc=Table()
        tumc['RA']=umc.ra.deg
        tumc['DEC']=umc.dec.deg
        tumc['_idx']=umcidx

        #return TableList([("skys",st),('matched',tmc),('unmatched',tumc)]),rad,maxMag,magCol,maxsources
        #return TableList([('skys',st),('matched',tmc),('unmatched',tumc)])
        return {'skys': st, 'matched': tmc, 'unmatched': tumc}

################################################################################
