from __future__ import with_statement
from __future__ import absolute_import
from __future__ import print_function
import sys


try:
    import numpy
    import reflex
    from pipeline_product import PipelineProduct
    import pipeline_display
    import reflex_plot_widgets

    from matplotlib import gridspec, pylab, pyplot, transforms
    import pdb  # for debugging
    from collections import defaultdict  # to make dictionary of lists
    import_success = True
    
except ImportError:
    import_success = False
    print("Error importing modules pyfits, wx, matplotlib, numpy")

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


def paragraph(text, width=None):
    """ wrap text string into paragraph
       text:  text to format, removes leading space and newlines
       width: if not None, wraps text, not recommended for tooltips as
              they are wrapped by wxWidgets by default
    """
    import textwrap
    if width is None:
        return textwrap.dedent(text).replace('\n', ' ').strip()
    else:
        return textwrap.fill(textwrap.dedent(text), width=width)


class DataPlotterManager(object):
    """
    This class must be added to the PipelineInteractiveApp with setPlotManager
    It must have following member functions which will be called by the app:
     - setInteractiveParameters(self)
     - readFitsData(self, fitsFiles):
     - addSubplots(self, figure):
     - plotProductsGraphics(self, figure, canvas)
    Following members are optional:
     - setWindowHelp(self)
     - setWindowTitle(self)
    """

    # static members
    
    recipe_name = "hawki_standard_process"
    img_cat = "BASIC_CALIBRATED_STD" # individual calibrated frames, no stacks made
    
    mstd_a_cat = "MATCHSTD_ASTROM"
    mstd_p_cat = "MATCHSTD_PHOTOM"

    def setWindowTitle(self):
        return self.recipe_name+"_interactive"

    def setInteractiveParameters(self):
        """
        This function specifies which are the parameters that should be presented
        in the window to be edited.  Note that the parameter has to also be 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.
        """

        # Show all recipe params 
        return [
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="minphotom",
                                   group="hawki_standard_process", 
                                   description="Minimum stars for photometry solution. [1]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="savemstd",
                                   group="hawki_standard_process", 
                                   description="Save matched standard catalogues?. [FALSE]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cdssearch_astrom",
                                   group="hawki_standard_process", 
                                   description="CDS astrometric catalogue. <none | 2mass | usnob | ppmxl | wise> [none]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cdssearch_photom",
                                   group="hawki_standard_process", 
                                   description="CDS photometric catalogue. <none | 2mass | ppmxl> [none]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_ipix",
                                   group="hawki_standard_process", 
                                   description="Minimum pixel area for each detected object. [10]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_thresh",
                                   group="hawki_standard_process", 
                                   description="Detection threshold in sigma above sky. [1.5]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_icrowd",
                                   group="hawki_standard_process", 
                                   description="Use deblending?. [TRUE]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_rcore",
                                   group="hawki_standard_process", 
                                   description="Value of Rcore in pixels. [10.0]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_nbsize",
                                   group="hawki_standard_process", 
                                   description="Background smoothing box size. [128]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="psm_ipix",
                                   group="hawki_standard_process", 
                                   description="Minimum pixel area for each detected object. [10]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="psm_niter",
                                   group="hawki_standard_process", 
                                   description="Number of iterations in pawsky mask. [5]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="psm_thresh",
                                   group="hawki_standard_process", 
                                   description="Detection threshold in sigma above sky. [1.5]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="psm_nbsize",
                                   group="hawki_standard_process", 
                                   description="Background smoothing box size. [256]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="psm_smkern",
                                   group="hawki_standard_process", 
                                   description="Smoothing kernel size (pixels). [2.0]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cacheloc",
                                   group="hawki_standard_process", 
                                   description="Location of standard star cache [.]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="magerrcut",
                                   group="hawki_standard_process", 
                                   description="Magnitude error cut [100.0]")
            ]

    def readFitsData(self, fitsFiles):
        """
        This function should be used to read and organize the raw fits files
        produced by the recipes.
        It receives as input a list of reflex.FitsFiles
        """

        # frames is a dict of keyword/list pairs where elements of list are PipelineProducts
        #  it contains all FITS files in the input parameter list

        self.std_frames = defaultdict(list)

        for f in fitsFiles:
            if (f.category == self.img_cat):
                self.std_frames[self.img_cat].append(PipelineProduct(f))
            if (f.category == self.mstd_a_cat):
                self.std_frames[self.mstd_a_cat].append(PipelineProduct(f))
            if (f.category == self.mstd_p_cat):
                self.std_frames[self.mstd_p_cat].append(PipelineProduct(f))

        if (len(self.std_frames[self.img_cat])) > 0:

            self.std_img_found = True
            self.n_std_frames = len(self.std_frames[self.img_cat])
            self.cur_std_frame = 0
            self.n_extn = len(self.std_frames[self.img_cat][0].hdulist())-1  # number of extensions, assumed to be same for all fitsFiles
            if (self.n_extn > 4):
                self.n_extn = 4 # Adaptiv optic extensions are present in the file

            # Don't read the individual calibrated standard images in here 
            #   for memory/performance reasons. Read them in below as needed.
            #   There could be a lot of frames and user may not want to see all of them.

            if (len(self.std_frames[self.mstd_a_cat])) > 0:
                self.std_mstd_found = True

                if (len(self.std_frames[self.img_cat]) != len(self.std_frames[self.mstd_a_cat])):
                    raise RuntimeError("Number of science images != number of matched astrometric standard catalogues!")
                if (len(self.std_frames[self.img_cat]) != len(self.std_frames[self.mstd_p_cat])):
                    raise RuntimeError("Number of science images != number of matched photometric standard catalogues!")

                # Sort the std frames and std mstd_a frames using PIPEFILE keyword
                # This will implicitly associate the sci frames with the mstd frames by using indices
                #   e.g. the mstd astrom cat for self.sci_frames[self.img_cat][0] ("exp_1.fits") 
                #         is self.sci_frames[self.mstd_a_cat][0] ("mstd_a0.fits")
            
                self.std_frames[self.img_cat].sort(key=lambda foo: foo.all_hdu[0].header['PIPEFILE'])
                self.std_frames[self.mstd_a_cat].sort(key=lambda foo: foo.all_hdu[0].header['PIPEFILE'])
                self.std_frames[self.mstd_p_cat].sort(key=lambda foo: foo.all_hdu[0].header['PIPEFILE'])

                # Read in FITS binary data like this:
                # table = self.std_frames[self.mstd_a_cat][i].all_hdu[i_ext+1].data
                # But do it only as needed
            else:
                self.std_mstd_found = False

            # re-define eso-rex's pipeline_display plotting functions to enable callbacks
            self._add_subplots = self._add_subplots
            self._plot = self._data_plot

            # Define radio button options
            self.left_opts = {'STD frames':0} 
            self.mid_opts = {'Image':0,'Assess matched astrom stds':1,
                             'Histogram of matched astrom stds':2,
                             'Assess matched photom stds':3,
                             'Histogram of matched photom stds':4}
            self.right_opts = {"Click to\nadvance to\nnext item\n(if available)"}

            # Set the initial radio button selections (0 for mid)
            self.mid_label = [key for key, value in self.mid_opts.items() if value == 0][0]

        else:
            # Set the plotting functions to NODATA ones
            self._add_subplots = self._add_nodata_subplots
            self._plot = self._nodata_plot

    def addSubplots(self, figure):
        self._add_subplots(figure)

    def plotProductsGraphics(self):
        self._plot()

    def plotWidgets(self) :
        widgets = list()

        # Radio buttons 
        # Only show them if at least one std frame is found
        if ((self.std_img_found is True)):
            self.radiobutton_left = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton_left, 
                                                                                self.setRadioCallback_left, 
                                                                                self.left_opts,
                                                                                0, 
                                                                                title='Select group:')
            widgets.append(self.radiobutton_left)

            # pull out the keys from the dict() of button options sorted by value
            mid_labels = [key for key,value in sorted(self.mid_opts.items(),key= lambda k: k[1])]
            self.radiobutton_mid = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton_mid, 
                                                                               self.setRadioCallback_mid, 
                                                                               mid_labels,
                                                                               self.mid_opts.get(self.mid_label), 
                                                                               title='Select item in group :')
            widgets.append(self.radiobutton_mid)

            self.radiobutton_right = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton_right, 
                                                                                 self.setRadioCallback_right, 
                                                                                 self.right_opts,
                                                                                 0, title='')
            widgets.append(self.radiobutton_right)

            # Adjust size of button boxes and font size of labels
            for i in range(len(widgets)):
                pos = widgets[i].rbuttons.ax.get_position()
                widgets[i].rbuttons.ax.set_position(transforms.Bbox([[pos.x0,pos.y0-0.01],[pos.x1, 0.97]]  ) )
                for j in range(len(widgets[i].rbuttons.labels)):
                    widgets[i].rbuttons.labels[j].set_fontsize(11)


        return widgets

    def setRadioCallback_left(self, label) :

        return # do nothing, only one option

    def setRadioCallback_mid(self, label) :

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

    def setRadioCallback_right(self, label) :

        # advance (or wrap) frame number by one
        self.cur_std_frame += 1
        if (self.cur_std_frame == (self.n_std_frames)):
            self.cur_std_frame = 0
        
        self._plot()

    def _add_subplots(self, figure):
      
        self.img_plot = []
        self.mstd_plot = []
        if ((self.std_img_found is True)):  # at least one std img found
            gs = gridspec.GridSpec(9, 4)
            gs.update(hspace=0.7)  # make space so axis labels dont overlap
            self.axradiobutton_left = figure.add_subplot(gs[0,0])
            self.axradiobutton_mid = figure.add_subplot(gs[0,1:3])
            self.axradiobutton_right = figure.add_subplot(gs[0,3])

            self.img_plot.append(figure.add_subplot(gs[1:5,0:2]))
            self.img_plot.append(figure.add_subplot(gs[1:5,2:4]))
            self.img_plot.append(figure.add_subplot(gs[5:9,2:4]))
            self.img_plot.append(figure.add_subplot(gs[5:9,0:2]))

            # Move ticks to rhs for readability
            self.img_plot[1].yaxis.tick_right()
            self.img_plot[2].yaxis.tick_right()

            self.mstd_plot.append(figure.add_subplot(gs[1:3,0:2]))
            self.mstd_plot.append(figure.add_subplot(gs[3:5,0:2]))
            self.mstd_plot.append(figure.add_subplot(gs[1:3,2:4]))
            self.mstd_plot.append(figure.add_subplot(gs[3:5,2:4]))
            self.mstd_plot.append(figure.add_subplot(gs[5:7,2:4]))
            self.mstd_plot.append(figure.add_subplot(gs[7:9,2:4]))
            self.mstd_plot.append(figure.add_subplot(gs[5:7,0:2]))
            self.mstd_plot.append(figure.add_subplot(gs[7:9,0:2]))

            # Move ticks to rhs for readability
            self.mstd_plot[2].yaxis.tick_right()
            self.mstd_plot[3].yaxis.tick_right()
            self.mstd_plot[4].yaxis.tick_right()
            self.mstd_plot[5].yaxis.tick_right()

            # Keep track if subplots have been repositioned
            self.mstd_repositioned = [False]*8
                                    
            # Initially, turn off tick labels for scatterplots
            for i in range(len(self.mstd_plot)):
                pylab.setp(self.mstd_plot[i].get_xticklabels(), visible = False)
                pylab.setp(self.mstd_plot[i].get_yticklabels(), visible = False)
                    
        else:
            gs = gridspec.GridSpec(2, 2)
            self.img_plot.append(figure.add_subplot(gs[0,0]))
            self.img_plot.append(figure.add_subplot(gs[0,1]))
            self.img_plot.append(figure.add_subplot(gs[1,0]))
            self.img_plot.append(figure.add_subplot(gs[1,1]))
            
    def _data_plot(self):

        # Get filter name from PHU of first file; assume it same for the rest
        try:
            filt_name = self.std_frames[self.img_cat][0].all_hdu[0].header['HIERARCH ESO INS FILT1 NAME']
        except:
            filt_name = ''
            
        for i in range(self.n_extn):

            if (self.mid_opts[self.mid_label] == 0):  # show an image
                
                # turn off scatterplot axes visibility
                self.mstd_plot[2*i].cla()
                self.mstd_plot[2*i+1].cla()
                self.mstd_plot[2*i].tooltip=''
                self.mstd_plot[2*i+1].tooltip=''
                self.mstd_plot[2*i].set_visible(False)
                self.mstd_plot[2*i+1].set_visible(False)

                # clear image frame and make it visible
                self.img_plot[i].cla()  
                self.img_plot[i].tooltip=''
                self.img_plot[i].set_visible(True)  
                for j in range(len(self.img_plot)):
                    if (j==0 or j == 1):
                        self.img_plot[j].set_xlabel(' ')
                    if (j==1 or j == 2):
                        self.img_plot[j].set_ylabel(' ')
                    pylab.setp(self.img_plot[j].get_xticklabels(), visible = True)
                    pylab.setp(self.img_plot[j].get_yticklabels(), visible = True)

                # Setup the selected image and display it
                imgdisp = pipeline_display.ImageDisplay()
                imgdisp.setAspect('equal')
                imgdisp.setLabels('X', 'Y')
                
                chip_name = self.std_frames[self.img_cat][self.cur_std_frame].all_hdu[i+1].header['EXTNAME']
                title = "Cal. Std Frame {} {}/{} {}".format(filt_name,self.cur_std_frame+1,self.n_std_frames,chip_name)
                # Try reading image
                try:
                    temp = self.std_frames[self.img_cat][self.cur_std_frame]
                    temp.readImage(i+1)
                    imgdisp.display(self.img_plot[i], title, "Calibrated Standard Frame:\n" + 
                                    temp.fits_file.name, temp.image)

                except IndexError:
                    self.img_plot[i].set_axis_off()
                    text_nodata = "No standard image found for this chip/extension."
                    self.img_plot[i].text(0.1, 0.5, text_nodata, color='#11557c',
                                          fontsize=18, ha='left', va='center', alpha=1.0,
                                          transform = self.img_plot[i].transAxes)
                    self.img_plot[i].tooltip = 'No data found'
                    continue  # go to next extension

            elif ((self.mid_opts[self.mid_label] == 1) or 
                  (self.mid_opts[self.mid_label] == 2)):    # show matched astrom stds

                # If this is first request for mstd astrom, 
                #   reposition the subplots so that axes touch and we have more room
                # We have to do it here because windows aren't rendered inside the _add_subplots function
                if (self.mstd_repositioned[2*i] == False):
                    pos = self.mstd_plot[2*i].get_position()
                    pos_new = [pos.x0, pos.y0-0.1*pos.height, pos.width, pos.height]
                    self.mstd_plot[2*i].set_position(pos_new)
                    self.mstd_repositioned[2*i] = True
                if (self.mstd_repositioned[2*i+1] == False):
                    pos = self.mstd_plot[2*i+1].get_position()
                    pos_new = [pos.x0, pos.y0+0.1*pos.height, pos.width, pos.height]
                    self.mstd_plot[2*i+1].set_position(pos_new)
                    self.mstd_repositioned[2*i+1] = True

                self.img_plot[i].cla()  
                self.img_plot[i].tooltip=''
                self.img_plot[i].set_visible(False)  

                # Turn on scatterplot axes 
                self.mstd_plot[2*i].cla()
                self.mstd_plot[2*i+1].cla()
                self.mstd_plot[2*i].tooltip=''
                self.mstd_plot[2*i+1].tooltip=''
                self.mstd_plot[2*i].set_visible(True)
                self.mstd_plot[2*i+1].set_visible(True)
                for j in range(len(self.mstd_plot)):
                    if (j%2 == 1):
                        pylab.setp(self.mstd_plot[j].get_xticklabels(), visible = True)
                    else:
                        pylab.setp(self.mstd_plot[j].get_xticklabels(), visible = False)
                    pylab.setp(self.mstd_plot[j].get_yticklabels(), visible = True)

                # Define xtitle
                if ((self.mid_opts[self.mid_label]==1) and ((i == 2) or (i == 3))) :
                    xtitle = "Row number of matched standard"
                else:
                    xtitle = " "

                chip_name = self.std_frames[self.img_cat][self.cur_std_frame].all_hdu[i+1].header['EXTNAME']
                title = "Cal. Std Frame {} {}/{} {}".format(filt_name,self.cur_std_frame+1,self.n_std_frames,chip_name)
                try:
                    table = self.std_frames[self.mstd_a_cat][self.cur_std_frame].all_hdu[i+1].data
                    filename = self.std_frames[self.mstd_a_cat][self.cur_std_frame].fits_file.name
                except IndexError:
                    text_nodata = "No valid matched astrom standard\ncatalog found for this chip."
                    for k in range(2):
                        self.mstd_plot[2*i+k].text(0.1, 0.5, text_nodata, color='#11557c',
                                                   fontsize=12, ha='left', va='center', alpha=1.0,
                                                   transform = self.mstd_plot[2*i+k].transAxes)
                        self.mstd_plot[2*i+k].tooltip = 'No data found'
                        self.mstd_plot[2*i+k].set_xlabel(xtitle)
                    continue # go to next extension

                # Check to make sure there is at least one row
                if (self.std_frames[self.mstd_a_cat][self.cur_std_frame].all_hdu[i+1].header['NAXIS2'] == 0):
                    text_nodata = "No valid matched astrom standard\ncatalog found for this chip."
                    for k in range(2):
                        self.mstd_plot[2*i+k].text(0.1, 0.5, text_nodata, color='#11557c',
                                                   fontsize=12, ha='left', va='center', alpha=1.0,
                                                   transform=self.mstd_plot[2*i+k].transAxes)
                        self.mstd_plot[2*i+k].tooltip = 'No data found'
                        self.mstd_plot[2*i+k].set_xlabel(xtitle)
                    continue # go to next extension

                # Show scatter plot
                if (self.mid_opts[self.mid_label]==1):

                    # Configure and display top plot of delta RA, if the column exists
                    #  (if WCS fitting fails, then column is missing)
                    try:
                        x_top = numpy.linspace(1,len(table['diffRA']), num = len(table['diffRA']))
                        y_top = numpy.cos(table['Dec']*numpy.pi/180.0)*table['diffRA'] * 3600.0 # in arcseconds
                    except KeyError:
                        text_nodata = "No valid matched astrom standard \ncatalog found for this chip."
                        self.mstd_plot[2*i].text(0.1, 0.5, text_nodata, color='#11557c',
                                                 fontsize=12, ha='left', va='center', alpha=1.0,
                                                 transform=self.mstd_plot[2*i+k].transAxes)
                        self.mstd_plot[2*i].tooltip = 'No data found'
                        self.mstd_plot[2*i].set_xlabel(xtitle)
                        continue # go to next extension

                    err_top = 0.0 * y_top
                
                    scat_top = pipeline_display.ScatterDisplay()
                    delta_x = max(x_top) - min(x_top)
                    scat_top.xLim = min(x_top)-0.11*delta_x, max(x_top)+0.11*delta_x
                    delta_y = max(y_top) - min(y_top)
                    scat_top.yLim = min(y_top)-0.11*delta_y, max(y_top)+0.11*delta_y
                    y_max = max([max(y_top),abs(min(y_top))])
                    if y_max > 1.0 : 
                        tool_tip = " WARNING: Difference in coord is > 1.0 arcsec!\n"
                    else:
                        scat_top.yLim = -1.1,1.1
                        tool_tip = "Matched astrometric standard catalogue:\n"
                    
                    scat_top.setLabels(" ",r'$\Delta\alpha*cos(\delta)$ ["]')
                    scat_top.display(self.mstd_plot[2*i],
                                     title, tool_tip + filename, 
                                     x_top, y_top, err_top)

                    # Configure and display top plot of delta Dec, if the column exists
                    #  (if WCS fitting fails, then column is missing)
                    try:
                        x_bot = numpy.linspace(1,len(table['diffDec']), num = len(table['diffDec']))
                        y_bot = table['diffDec'] * 3600.0 # in arcseconds
                    except KeyError:
                        text_nodata = "No valid matched astrom standard \ncatalog found for this chip."
                        self.mstd_plot[2*i+1].text(0.1, 0.5, text_nodata, color='#11557c',
                                                   fontsize=12, ha='left', va='center', alpha=1.0,
                                                   transform=self.mstd_plot[2*i+k].transAxes)
                        self.mstd_plot[2*i+1].tooltip = 'No data found'
                        self.mstd_plot[2*i+1].set_xlabel(xtitle)
                        continue # go to next extension

                    err_bot = 0.0 * y_bot

                    scat_bot = pipeline_display.ScatterDisplay()
                    scat_bot.xLim = scat_top.xLim
                    delta_y = max(y_bot) - min(y_bot)
                    scat_bot.yLim = min(y_bot)-0.11*delta_y, max(y_bot)+0.11*delta_y
                    y_max = max([max(y_bot),abs(min(y_bot))])
                    if y_max > 1.0 : 
                        tool_tip = " WARNING: Difference in coord is > 1.0 arcsec! \n"
                    else:
                        scat_bot.yLim = -1.1,1.1
                        tool_tip = "Matched astrometric standard catalogue:\n"
                        
                    scat_bot.setLabels(xtitle,r'$\Delta\delta$ ["]')
                    scat_bot.display(self.mstd_plot[2*i+1],
                                     " ", tool_tip + filename, 
                                     x_bot, y_bot, err_bot)
                # Show histogram
                if (self.mid_opts[self.mid_label]==2):

                    self.mstd_plot[2*i].cla()
                    self.mstd_plot[2*i+1].cla()
                    self.mstd_plot[2*i].tooltip=''
                    self.mstd_plot[2*i+1].tooltip=''
                    self.mstd_plot[2*i].set_visible(False)
                    self.mstd_plot[2*i+1].set_visible(False)
                        
                    self.img_plot[i].cla()  
                    self.img_plot[i].tooltip=''
                    self.img_plot[i].set_visible(True)  
                    for j in range(len(self.img_plot)):
                        pylab.setp(self.img_plot[j].get_xticklabels(), visible = True)
                        pylab.setp(self.img_plot[j].get_yticklabels(), visible = True)

                    try:
                        delta_dec = table['diffDec'] * 3600.0 # in arcseconds
                        delta_ra = numpy.cos(table['Dec']*numpy.pi/180.0)*table['diffRA'] * 3600.0 # in arcseconds
                    except KeyError:
                        text_nodata = "No valid matched astrom standard \ncatalog found for this chip."
                        for k in range(2):
                            self.mstd_plot[2*i+k].text(0.1, 0.5, text_nodata, color='#11557c',
                                                       fontsize=12, ha='left', va='center', alpha=1.0,
                                                       transform=self.mstd_plot[2*i+k].transAxes)
                            self.mstd_plot[2*i+k].tooltip = 'No data found'
                            self.mstd_plot[2*i+k].set_xlabel(xtitle)
                        continue # go to next extension

                    # some entries are NaN if reference catalog doesnt have valid coords
                    delta_ra_valid = delta_ra[(numpy.isfinite(delta_ra) & numpy.isfinite(delta_dec))]
                    delta_dec_valid = delta_dec[(numpy.isfinite(delta_ra) & numpy.isfinite(delta_dec))]

                    r = numpy.sqrt(delta_ra_valid**2 + delta_dec_valid**2)

                    med = numpy.median(r)
                    mad = MAD(r)
                    n, bins, patches = self.img_plot[i].hist(r)
                    self.img_plot[i].axis('tight')

                    if ((i ==0) or (i == 1)):
                        self.img_plot[i].set_xlabel('')
                    elif ((i == 2) or (i == 3)):
                        self.img_plot[i].set_xlabel(r'$\Delta\Theta=$'+r'$\sqrt{[cos(\delta)*\Delta\alpha]^2 + \Delta\delta^2}$'+' ["]')

                    self.img_plot[i].set_ylabel('Frequency')
                    self.img_plot[i].set_title(title,fontweight='semibold', fontsize=12)
                    self.img_plot[i].tooltip = "Histogram with 10 bins over entire data range\nNumbers in legend are for whole data sample"
                    self.img_plot[i].text(0.65,0.9,'Med:  {:8.2f}'.format(med), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.65,0.8,'Mean: {:8.2f}'.format(numpy.mean(r)), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.65,0.7,'MAD:  {:8.2f}'.format(mad), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.65,0.6,'RMS:  {:8.2f}'.format(numpy.std(r)), 
                                          transform=self.img_plot[i].transAxes,color='red')

            elif ((self.mid_opts[self.mid_label] == 3) or 
                  (self.mid_opts[self.mid_label] == 4)):    # show matched photom stds

                # there is just one scatter plot per chip, so use img_plot subplot

                self.mstd_plot[2*i].cla()
                self.mstd_plot[2*i+1].cla()
                self.mstd_plot[2*i].tooltip=''
                self.mstd_plot[2*i+1].tooltip=''
                self.mstd_plot[2*i].set_visible(False)
                self.mstd_plot[2*i+1].set_visible(False)

                self.img_plot[i].cla()  
                self.img_plot[i].tooltip=''
                self.img_plot[i].set_visible(True)  
                self.img_plot[i].axis('auto')
                for j in range(len(self.img_plot)):
                    pylab.setp(self.img_plot[j].get_xticklabels(), visible = True)
                    pylab.setp(self.img_plot[j].get_yticklabels(), visible = True)

                # Define xtitle
                if ((self.mid_opts[self.mid_label]==3) and ((i == 2) or (i == 3))) :
                    xtitle = "Row number of matched standard"
                else:
                    xtitle = " "

                chip_name = self.std_frames[self.img_cat][self.cur_std_frame].all_hdu[i+1].header['EXTNAME']
                title = "Cal. Std Frame {} {}/{} {}".format(filt_name,self.cur_std_frame+1,self.n_std_frames,chip_name)
                try:
                    table = self.std_frames[self.mstd_p_cat][self.cur_std_frame].all_hdu[i+1].data
                    filename = self.std_frames[self.mstd_p_cat][self.cur_std_frame].fits_file.name
                except IndexError:
                    text_nodata = "No valid matched photom standard\n catalog found for this chip."
                    self.img_plot[i].text(0.1, 0.5, text_nodata, color='#11557c',
                                          fontsize=12, ha='left', va='center', alpha=1.0,
                                          transform=self.img_plot[i].transAxes)
                    self.img_plot[i].tooltip = 'No data found'
                    self.img_plot[i].set_xlabel(xtitle)
                    continue # go to next extension

                # Check to make sure there is at least one row
                if (self.std_frames[self.mstd_p_cat][self.cur_std_frame].all_hdu[i+1].header['NAXIS2'] == 0):
                    text_nodata = "No valid matched photom standard\ncatalog found for this chip."
                    self.img_plot[i].text(0.1, 0.5, text_nodata, color='#11557c',
                                          fontsize=12, ha='left', va='center', alpha=1.0,
                                          transform=self.img_plot[i].transAxes)
                    self.img_plot[i].tooltip = 'No data found'
                    self.img_plot[i].set_xlabel(xtitle)
                    continue

                # Show scatter plot
                if (self.mid_opts[self.mid_label]==3):
                    try:
                        x = numpy.linspace(1,len(table['dm5']), num = len(table['dm5']))
                        y = table['dm5']  # difference in magnitudes (measured - reference), use aper5
                    except KeyError:
                        text_nodata = "No valid matched photom standard\ncatalog found for this chip."
                        self.img_plot[i].text(0.1, 0.5, text_nodata, color='#11557c',
                                              fontsize=12, ha='left', va='center', alpha=1.0,
                                              transform=self.img_plot[i].transAxes)
                        self.img_plot[i].tooltip = 'No data found'
                        self.img_plot[i].set_xlabel(xtitle)
                        continue
                    
                    err = 0.0 * y
                
                    scat = pipeline_display.ScatterDisplay()

                    tool_tip = "Matched photometric standards catalogue:\n"
                    delta_x = max(x) - min(x)
                    scat.xLim = min(x)-0.11*delta_x, max(x)+0.11*delta_x

                    delta_y = max(y[numpy.isfinite(y)]) - min(y[numpy.isfinite(y)])
                    scat.yLim = min(y[numpy.isfinite(y)])-0.11*delta_y, max(y[numpy.isfinite(y)])+0.11*delta_y

                    scat.setLabels(xtitle,'Magnitude zero point')
                    scat.display(self.img_plot[i],
                                 title, tool_tip + filename, 
                                 x, y, err)

                # Show histogram plot of non-NaN map zpt entries in table
                elif (self.mid_opts[self.mid_label]==4):
                    try:
                        x = table['dm5']  # difference in magnitudes (measured - reference), use aper5
                    except KeyError:
                        text_nodata = "No valid matched photom standard\ncatalog found for this chip."
                        self.img_plot[i].text(0.1, 0.5, text_nodata, color='#11557c',
                                              fontsize=12, ha='left', va='center', alpha=1.0,
                                              transform=self.img_plot[i].transAxes)
                        self.img_plot[i].tooltip = 'No data found'
                        self.img_plot[i].set_xlabel(xtitle)
                        continue
                    
                    # some entries are NaN if reference catalog doesnt have mag in the same band as data being reduced
                    x = x[numpy.isfinite(x)]  
                    med = numpy.median(x)
                    mad = MAD(x)
                    n, bins, patches = self.img_plot[i].hist(x)
                    self.img_plot[i].axis('tight')

                    if ((i ==0) or (i == 1)):
                        self.img_plot[i].set_xlabel('')
                    elif ((i == 2) or (i == 3)):
                        self.img_plot[i].set_xlabel('MagZPT [mag]')

                    self.img_plot[i].set_ylabel('Frequency')
                    self.img_plot[i].tooltip = "Histogram with 10 bins over entire data range\nNumbers in legend are for whole data sample"
                    self.img_plot[i].set_title(title,fontweight='semibold', fontsize=12)
                    self.img_plot[i].text(0.05,0.9,'Med:  {:8.2f}'.format(med), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.05,0.8,'Mean: {:8.2f}'.format(numpy.mean(x)), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.05,0.7,'MAD:  {:8.2f}'.format(mad), 
                                          transform=self.img_plot[i].transAxes,color='red')
                    self.img_plot[i].text(0.05,0.6,'RMS:  {:8.2f}'.format(numpy.std(x)), 
                                          transform=self.img_plot[i].transAxes,color='red')

    def _add_nodata_subplots(self, figure):
        self.img_plot = figure.add_subplot(1,1,1)

    def _nodata_plot(self):
        # could be moved to reflex library?
        self.img_plot.set_axis_off()
        text_nodata = "Data not found. Input files should contain this" \
                       " type:\n%s" % self.img_cat
        self.img_plot.text(0.1, 0.6, text_nodata, color='#11557c',
                      fontsize=18, ha='left', va='center', alpha=1.0)
        self.img_plot.tooltip = 'No data found'


    def setWindowHelp(self):
      help_text = """
This is an interactive window which help asses the quality of the execution of a recipe.
"""
      return help_text


#This is the 'main' function
if __name__ == '__main__':
    from reflex_interactive_app import PipelineInteractiveApp

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

    # get inputs from the command line
    interactive_app.parse_args()

    #Check if import failed or not
    if not import_success:
        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.set_continue_mode()

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