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_science_postprocess"
    img_cat = "TILED_IMAGE" # One tiled image
    obj_cat = "TILED_OBJECT_CATALOGUE"

    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="nebulise",
                                   group="hawki_science_postprocess", 
                                   description="Nebulise the stacks before object detection?. [FALSE]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="minphotom",
                                   group="hawki_science_postprocess", 
                                   description="Minimum stars for photometry solution. [20]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cdssearch_astrom",
                                   group="hawki_science_postprocess", 
                                   description="CDS astrometric catalogue. <none | 2mass | usnob | ppmxl | wise> [none]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cdssearch_photom",
                                   group="hawki_science_postprocess", 
                                   description="CDS photometric catalogue. <none | 2mass | ppmxl> [none]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="stk_fast",
                                   group="hawki_science_postprocess", 
                                   description="Use fast stacking?. <fast | slow | auto> [auto]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="stk_nfst",
                                   group="hawki_science_postprocess", 
                                   description="Nframes before we switch to slow stack algorithm. [30]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="savemstd",
                                   group="hawki_science_postprocess", 
                                   description="Save matched standard catalogues?. [FALSE]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="neb_medfilt",
                                   group="hawki_science_postprocess", 
                                   description="Median filter size for nebuliser. [101]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="neb_linfilt",
                                   group="hawki_science_postprocess", 
                                   description="Median filter size for nebuliser. [33]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_ipix",
                                   group="hawki_science_postprocess", 
                                   description="Minimum pixel area for each detected object. [4]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_thresh",
                                   group="hawki_science_postprocess", 
                                   description="Detection threshold in sigma above sky. [2.5]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_icrowd",
                                   group="hawki_science_postprocess", 
                                   description="Use deblending?. [TRUE]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_rcore",
                                   group="hawki_science_postprocess", 
                                   description="Value of Rcore in pixels. [10.0]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cat_nbsize",
                                   group="hawki_science_postprocess", 
                                   description="Background smoothing box size. [64]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="cacheloc",
                                   group="hawki_science_postprocess", 
                                   description="Location of standard star cache. [.]"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="magerrcut",
                                   group="hawki_science_postprocess", 
                                   description="Cut in magnitude error [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.tile_frames = defaultdict(list)

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

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

            self.tile_img_found = True
            # assume there is only one tiled image, no need for sorting/matching/counting

            if (len(self.tile_frames[self.mstd_a_cat])) > 0:
                self.tile_mstd_found = True

                # There should only be one tile and one matchstd, but check that here
                if (len(self.tile_frames[self.img_cat]) != len(self.tile_frames[self.mstd_a_cat])):
                    raise RuntimeError("Number of tile images != number of matched astrometric standard catalogues!")
                if (len(self.tile_frames[self.img_cat]) != len(self.tile_frames[self.mstd_p_cat])):
                    raise RuntimeError("Number of tile images != number of matched photometric standard catalogues!")

            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 = {'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 = {'Yes':0, 'No':1}

            # Set the initial radio button selections (value 0 for left, 1 for right)
            self.left_label = [key for key, value in self.left_opts.items() if value == 0][0]
            self.right_label = [key for key, value in self.right_opts.items() if value == 1][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 tile image is found
        if ((self.tile_img_found is True)):
            left_labels = [key for key,value in sorted(self.left_opts.items(),key= lambda k: k[1])]
            self.radiobutton_left = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton_left, 
                                                                                self.setRadioCallback_left, 
                                                                                left_labels,
                                                                                self.left_opts.get(self.left_label), 
                                                                                title='Show:')
            widgets.append(self.radiobutton_left)

            right_labels = [key for key,value in sorted(self.right_opts.items(),key= lambda k: k[1])]
            self.radiobutton_right = reflex_plot_widgets.InteractiveRadioButtons(self.axradiobutton_right, 
                                                                                 self.setRadioCallback_right, 
                                                                                 right_labels,
                                                                                 self.right_opts.get(self.right_label), 
                                                                                 title='Overplot catalogued objs?')
            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) :

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

    def setRadioCallback_right(self, label) :

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

    def _add_subplots(self, figure):
      
        self.img_plot = []
        self.mstd_plot = []
        if ((self.tile_img_found is True)): 
            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:2])
            self.axradiobutton_right = figure.add_subplot(gs[0,3])

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

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

            # 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:
            # No data
            gs = gridspec.GridSpec(1, 1)
            self.img_plot.append(figure.add_subplot(gs[0,0]))
            
    def _data_plot(self):

        if (self.left_opts[self.left_label] == 0):  # show an image
                
            # turn off scatterplot axes visibility
            self.mstd_plot[0].cla()
            self.mstd_plot[1].cla()
            self.mstd_plot[0].tooltip = ''
            self.mstd_plot[1].tooltip = ''
            self.mstd_plot[0].set_visible(False)
            self.mstd_plot[1].set_visible(False)

            # clear image frame and make it visible
            self.img_plot[0].cla()  
            self.img_plot[0].tooltip = ''  
            self.img_plot[0].set_visible(True)  
            pylab.setp(self.img_plot[0].get_xticklabels(), visible = True)
            pylab.setp(self.img_plot[0].get_yticklabels(), visible = True)

            # Setup the selected image and display it
            imgdisp = pipeline_display.ImageDisplay()
            imgdisp.setAspect('equal')
            imgdisp.setLabels('X', 'Y')

            try: 
                is_nebulised = ((self.tile_frames[self.img_cat][0].all_hdu[0].header['HIERARCH ESO PRO REC2 PARAM1 VALUE']) == 'true')
            except KeyError:
                is_nebulised = False

            if (is_nebulised):
                tool_tip = paragraph("""Tiled and nebulised image""")
            else:
                tool_tip = paragraph("""Tiled image""")
            
            try:
                filt_name = self.tile_frames[self.img_cat][0].all_hdu[0].header['HIERARCH ESO INS FILT1 NAME']
                title = "Tile  Filter: {} ".format(filt_name)
            except KeyError:
                title = "Tile  Filter: {} ".format('null')

            # Try reading image
            try:
                temp = self.tile_frames[self.img_cat][0]
                temp.readImage(0)  # data is in part of PHU (file is not a MEF)
                if (self.right_opts[self.right_label] == 0):  # overplot objs
                    table = self.tile_frames[self.obj_cat][0].all_hdu[1].data
                    imgdisp.overplotScatter(
                        table['X_coordinate'],table['Y_coordinate'],
                        marker='+', size=10, color='red')
                    imgdisp.display(self.img_plot[0], title, tool_tip + ":\n" + 
                                    temp.fits_file.name, temp.image)
                else:
                    self.img_plot[0].cla()
                    imgdisp.display(self.img_plot[0], title, tool_tip + ":\n" + 
                                    temp.fits_file.name, temp.image)
                
            except IndexError:
                self.img_plot[0].set_axis_off()
                text_nodata = "No valid tiled image found."
                self.img_plot[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                      fontsize=18, ha='left', va='center', alpha=1.0,
                                      transform = self.img_plot[0].transAxes)
                self.img_plot[0].tooltip = 'No data found'
                return

        elif ((self.left_opts[self.left_label] == 1) or 
              (self.left_opts[self.left_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[0] == False):
                pos = self.mstd_plot[0].get_position()
                pos_new = [pos.x0, pos.y0+0.015*pos.height, pos.width, pos.height]
                self.mstd_plot[0].set_position(pos_new)
                self.mstd_repositioned[0] = True
            if (self.mstd_repositioned[1] == False):
                pos = self.mstd_plot[1].get_position()
                pos_new = [pos.x0, pos.y0-0.015*pos.height, pos.width, pos.height]
                self.mstd_plot[1].set_position(pos_new)
                self.mstd_repositioned[1] = True

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

            # Turn on scatterplot axes 
            self.mstd_plot[0].cla()
            self.mstd_plot[1].cla()
            self.mstd_plot[0].tooltip=''
            self.mstd_plot[1].tooltip=''
            self.mstd_plot[0].set_visible(True)
            self.mstd_plot[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
            xtitle = "Row number of matched standard"
            title = "Tile"

            try:
                table = self.tile_frames[self.mstd_a_cat][0].all_hdu[1].data
                filename = self.tile_frames[self.mstd_a_cat][0].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[k].text(0.1, 0.5, text_nodata, color='#11557c',
                                               fontsize=12, ha='left', va='center', alpha=1.0,
                                               transform = self.mstd_plot[k].transAxes)
                    self.mstd_plot[k].tooltip = 'No data found'
                    self.mstd_plot[k].set_xlabel("")
                    return

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

            # Show scatter plot
            if (self.left_opts[self.left_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."
                    self.mstd_plot[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                           fontsize=12, ha='left', va='center', alpha=1.0,
                                           transform=self.mstd_plot[0].transAxes)
                    self.mstd_plot[0].tooltip = 'No data found'
                    self.mstd_plot[0].set_xlabel(xtitle)
                    return

                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[0],
                                 title, tool_tip + filename, 
                                 x_top, y_top, err_top)

                # Configure and display bot 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[1].text(0.1, 0.5, text_nodata, color='#11557c',
                                               fontsize=12, ha='left', va='center', alpha=1.0,
                                               transform=self.mstd_plot[1].transAxes)
                    self.mstd_plot[1].tooltip = 'No data found'
                    self.mstd_plot[1].set_xlabel(xtitle)
                    return

                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[1],
                                 " ", tool_tip + filename, 
                                 x_bot, y_bot, err_bot)

            # Show histogram
            if (self.left_opts[self.left_label]==2):

                self.mstd_plot[0].cla()
                self.mstd_plot[1].cla()
                self.mstd_plot[0].tooltip=''
                self.mstd_plot[1].tooltip=''
                self.mstd_plot[0].set_visible(False)
                self.mstd_plot[1].set_visible(False)
                        
                self.img_plot[0].cla()  
                self.img_plot[0].tooltip = ''
                self.img_plot[0].set_visible(True)  
                pylab.setp(self.img_plot[0].get_xticklabels(), visible = True)
                pylab.setp(self.img_plot[0].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[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                               fontsize=12, ha='left', va='center', alpha=1.0,
                                               transform=self.mstd_plot[0].transAxes)
                        self.mstd_plot[0].tooltip = 'No data found'
                        self.mstd_plot[0].set_xlabel(xtitle)
                    return
                    
                # 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[0].hist(r)
                self.img_plot[0].axis('tight')

                self.img_plot[0].set_xlabel(r'$\Delta\Theta=$'+r'$\sqrt{[cos(\delta)*\Delta\alpha]^2 + \Delta\delta^2}$'+' ["]')

                self.img_plot[0].set_ylabel('Frequency')
                self.img_plot[0].set_title(title,fontweight='semibold', fontsize=12)
                self.img_plot[0].tooltip = 'Histogram with 10 bins over entire data range\nNumbers in legend are for whole data sample'
                self.img_plot[0].text(0.85,0.9,'Med:  {:8.2f}'.format(med), 
                                      transform=self.img_plot[0].transAxes,color='red')
                self.img_plot[0].text(0.85,0.8,'Mean: {:8.2f}'.format(numpy.mean(r)), 
                                      transform=self.img_plot[0].transAxes,color='red')
                self.img_plot[0].text(0.85,0.7,'MAD:  {:8.2f}'.format(mad), 
                                      transform=self.img_plot[0].transAxes,color='red')
                self.img_plot[0].text(0.85,0.6,'RMS:  {:8.2f}'.format(numpy.std(r)), 
                                      transform=self.img_plot[0].transAxes,color='red')
                
        elif ((self.left_opts[self.left_label] == 3) or # show matched photom stds
              (self.left_opts[self.left_label] == 4)):

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

            self.mstd_plot[0].cla()
            self.mstd_plot[1].cla()
            self.mstd_plot[0].tooltip=''
            self.mstd_plot[1].tooltip=''
            self.mstd_plot[0].set_visible(False)
            self.mstd_plot[1].set_visible(False)

            self.img_plot[0].cla()  
            self.img_plot[0].tooltip=''
            self.img_plot[0].set_visible(True)  
            self.img_plot[0].axis('auto')
            pylab.setp(self.img_plot[0].get_xticklabels(), visible = True)
            pylab.setp(self.img_plot[0].get_yticklabels(), visible = True)

            try:
                table = self.tile_frames[self.mstd_p_cat][0].all_hdu[1].data
                filename = self.tile_frames[self.mstd_p_cat][0].fits_file.name
            except IndexError:
                text_nodata = "No valid matched photom standard\n catalog found for this chip."
                self.img_plot[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                      fontsize=12, ha='left', va='center', alpha=1.0,
                                      transform=self.img_plot[0].transAxes)
                self.img_plot[0].tooltip = 'No data found'
                self.img_plot[0].set_xlabel("")
                return

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

            if (self.left_opts[self.left_label]==3): # show scatter plot

                xtitle = "Row number of matched standard"

                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."
                    self.img_plot[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                          fontsize=12, ha='left', va='center', alpha=1.0,
                                          transform=self.img_plot[0].transAxes)
                    self.img_plot[0].tooltip = 'No data found'
                    self.img_plot[0].set_xlabel("")
                    return
                
                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[0],
                             title, tool_tip + filename, 
                             x, y, err)
                
            elif ((self.left_opts[self.left_label] == 4)): # show  histo

                # Show histogram plot of non-NaN map zpt entries in table
                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[0].text(0.1, 0.5, text_nodata, color='#11557c',
                                          fontsize=12, ha='left', va='center', alpha=1.0,
                                          transform=self.img_plot[0].transAxes)
                    self.img_plot[0].tooltip = 'No data found'
                    self.img_plot[0].set_xlabel(xtitle)
                    return
                    
                # 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[0].hist(x)
                self.img_plot[0].axis('tight')

                self.img_plot[0].set_xlabel('MagZPT [mag]')

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