from __future__ import with_statement
import sys

try:
    import numpy
    import reflex
    from pipeline_product import PipelineProduct
    import pipeline_display
    import_success = True

except ImportError:
    import_success = False
    print "Error importing modules pyfits, wx, matplotlib, numpy"

class ScatterDisplayLocal(pipeline_display.ScatterDisplay):
    def setErrorBar(self, subplot, x, y, error):
        subplot.errorbar(x, y, yerr=error, fmt='-o')

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 = "kmo_wave_cal"
    det_img_wave_cat = "DET_IMG_WAVE"
    lcal_cat = "LCAL"

    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.
        """
        return [
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="order",
                    group="Wavelength Calibration", description="The wavelength polynomial order"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="dev_flip",
                    group="Wavelength Calibration", description="TRUE if the wavelengths are ascending on the detector from top to bottom"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="dev_disp",
                    group="Wavelength Calibration", description="The expected dispersion of the wavelength in microns/pixel"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="suppress_extension",
                    group="Wavelength Calibration", description="Suppress arbitrary filename extension"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="b_samples",
                    group="Wavelength Calibration", description="The number of samples in wavelength for the reconstructed cube"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="b_start",
                    group="Wavelength Calibration", description="The lowest wavelength to take into account when reconstructing"),
            reflex.RecipeParameter(recipe=self.recipe_name, displayName="b_end",
                    group="Wavelength Calibration", description="The highest wavelength to take into account when reconstructing"),
        ]

    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
        """
        # organize the files into a dictionary, here we assume we only have 
        # one file per category if there are more, one must use a
        # dictionary of lists
        self.frames = dict()
        for f in fitsFiles:
            self.frames[f.category] = PipelineProduct(f)

        # Two cases: the file category is found or not found.
        # Define the plotting functions in both cases
        if self.det_img_wave_cat in self.frames:
            self._add_subplots = self._add_subplots
            self._plot = self._data_plot
        else:
            self._add_subplots = self._add_nodata_subplots
            self._plot = self._nodata_plot

    def addSubplots(self, figure):
        """
        This function should be used to setup the subplots of the gui.  The the
        matplotlib documentation for a description of subplots.
        """
        self._add_subplots(figure)

    def plotProductsGraphics(self, figure, canvas):
        """
        This function should be used to plot the data onto the subplots.
        """
        self._plot(figure, canvas)

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

    def _add_subplots(self, figure):
        self.img_plot = []
        for i in range(18):
            self.img_plot.append(figure.add_subplot(8,3,i+1))
        self.argon_plot = figure.add_subplot(817)
        self.neon_plot = figure.add_subplot(818)

    def _data_plot_get_tooltip(self, fits_file, extension):
        key1 = fits_file.readKeyword('ESO QC ARC AR POS MEAN', extension)
        key2 = fits_file.readKeyword('ESO QC ARC AR FWHM MEAN', extension)
        key3 = fits_file.readKeyword('ESO QC ARC NE POS MEAN', extension)
        key4 = fits_file.readKeyword('ESO QC ARC NE FWHM MEAN', extension)
        
        # Fill the Argon Data for the scatter plot
        self.argon_pos_data.append(key1)
        self.argon_fwhm_data.append(key2)
        self.neon_pos_data.append(key3)
        self.neon_fwhm_data.append(key4)
        
        # Fill the Neon Data for the scatter plot

        # Create the tooltip
        tooltip = " \
                ESO QC ARC AR POS MEAN : %f \n \
                ESO QC ARC AR FWHM MEAN : %f \n \
                ESO QC ARC NE POS MEAN : %f \n \
                ESO QC ARC NE FWHM MEAN : %f \
        " % (key1,key2,key3,key4)
        return tooltip

    def _data_plot(self, figure, canvas):

        # Get the wished files
        det_img_wave = self.frames[self.det_img_wave_cat]
        lcal = self.frames[self.lcal_cat]

        # Initialise the data holders for the scatter plots
        self.argon_pos_data = []
        self.argon_fwhm_data = []
        self.neon_pos_data = []
        self.neon_fwhm_data = []

        # Setup the image display
        for i in range(18):
            imgdisp = pipeline_display.ImageDisplay()
            # Title gives the Detector number at the top of the column
            if i == 0:
                title = "Detector 1"
            elif i == 1:
                title = "Detector 2"
            elif i == 2:
                title = "Detector 3"
            else:
                title = ""
            
            # Y Label gives the angle at the start of the ROW
            if i in [0,3,6,9,12,15]:
                angle = det_img_wave.readKeyword('ESO PRO ROT NAANGLE', i+1)
                ylabel = "Angle %.0f d." % angle
            else:
                ylabel = ""

            # Read the image
            det_img_wave.readImage(i+1)

            # Display the image
            imgdisp.setLabels('', ylabel)
            imgdisp.display(self.img_plot[i], title, self._data_plot_get_tooltip(lcal,i+1), det_img_wave.image)
       
        scadsp = ScatterDisplayLocal()
        x = numpy.linspace(1, 18, num=18)
        scadsp.display(self.argon_plot, "Argon Lines", "Argon Lines Positions and FWH", x, self.argon_pos_data)
        scadsp.setErrorBar(self.argon_plot, x, self.argon_pos_data, self.argon_fwhm_data)

        scadsp = ScatterDisplayLocal()
        scadsp.display(self.neon_plot, "Neon Lines", "Neon Lines Positions and FWH", x, self.neon_pos_data)
        scadsp.setErrorBar(self.neon_plot, x, self.neon_pos_data, self.neon_fwhm_data)

    def _nodata_plot(self, figure, canvas):
        # 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.det_img_wave_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'

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

    # Create interactive application
    interactive_app = PipelineInteractiveApp()

    # 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()
