__author__ = 'labx'

import os, sys, numpy

from AnyQt.QtGui import QImage, QPixmap,  QTextCursor
from AnyQt.QtWidgets import QLabel, QWidget, QHBoxLayout, QMessageBox

from silx.gui.plot.ImageView import ImageView

import orangecanvas.resources as resources
from orangewidget import gui
from orangewidget.settings import Setting
from orangewidget.widget import Input, Output

from oasys2.widget.widget import OWAction
from oasys2.widget import gui as oasysgui
from oasys2.widget.util import congruence
from oasys2.widget.util.widget_objects import TriggerIn, OasysThicknessErrorsData
from oasys2.widget.util.widget_util import EmittingStream
from oasys2.widget.gui import Styles
from oasys2.canvas.util.canvas_util import add_widget_parameters_to_module

from orangecontrib.shadow4.widgets.gui.ow_automatic_element import AutomaticElement
from orangecontrib.shadow4.util.shadow4_objects import ShadowData
from orangecontrib.shadow4.util.shadow4_util import ShadowCongruence, ShadowPlot
from orangecontrib.shadow4.util.python_script import PythonScript
from orangecontrib.shadow4.util.shadow4_util import TriggerToolsDecorator

from hybrid_methods.coherence.hybrid_screen import HybridListener, \
    HybridCalculationType, HybridPropagationType, HybridDiffractionPlane, \
    HybridInputParameters, HybridCalculationResult, HybridGeometryAnalysis

from shadow4_advanced.hybrid.s4_hybrid_screen import S4HybridOE, S4HybridBeam, S4HybridScreen, S4HybridScreenElement

class HybridScreen(AutomaticElement, TriggerToolsDecorator, HybridListener):

    class Inputs:
        shadow_data          = Input("Shadow Data", ShadowData, default=True, auto_summary=False)
        trigger              = TriggerToolsDecorator.get_trigger_input()
        thickness_error_data = Input("Thickness Errors Data", OasysThicknessErrorsData, default=True, auto_summary=False)

    class Outputs:
        shadow_data_ff = Output("Output Data (Far Field)", ShadowData, default=True, auto_summary=False)
        shadow_data_nf = Output("Output Data (Near Field)", ShadowData, default=True, auto_summary=False)
        trigger        = TriggerToolsDecorator.get_trigger_output()

    name = "Hybrid Screen"
    description = "Shadow HYBRID: Hybrid Screen"
    icon = "icons/hybrid_screen.png"
    maintainer = "Luca Rebuffi and Xianbo Shi"
    maintainer_email = "lrebuffi(@at@)anl.gov, xshi(@at@)aps.anl.gov"
    priority = 1.0
    category = "HYBRID"
    keywords = ["data", "file", "load", "read"]

    want_control_area = 1
    want_main_area = 1

    view_type = Setting(1)
    send_original_beam = Setting(0)

    diffraction_plane = Setting(1)
    calculation_type = Setting(2)

    propagation_type = Setting(0)

    n_bins_x = Setting(100)
    n_bins_z = Setting(100)
    n_peaks = Setting(10)
    fft_n_pts = Setting(1e5)

    analyze_geometry = Setting(1)

    crl_error_profiles = Setting([])
    crl_material_data = Setting(0)
    crl_material = Setting("Be")
    crl_delta = Setting(1e-6)
    crl_scaling_factor = Setting(1.0)
    crl_coords_to_m = Setting(1.0)
    crl_thickness_to_m = Setting(1.0)

    __input_data = None
    __plotted_data = None

    TABS_AREA_HEIGHT = 560
    CONTROL_AREA_WIDTH = 405

    IMAGE_WIDTH = 860
    IMAGE_HEIGHT = 545

    def __init__(self):
        super().__init__()

        self.runaction = OWAction("Run Hybrid", self)
        self.runaction.triggered.connect(self.run_hybrid)
        self.addAction(self.runaction)

        self.controlArea.setFixedWidth(self.CONTROL_AREA_WIDTH)

        button_box = oasysgui.widgetBox(self.controlArea, "", addSpace=False, orientation="horizontal", width=self.CONTROL_AREA_WIDTH-5)

        button = gui.button(button_box, self, "Run HYBRID", callback=self.run_hybrid)
        button.setStyleSheet(Styles.button_blue)

        main_tabs = oasysgui.tabWidget(self.mainArea)
        plot_tab = oasysgui.createTabPage(main_tabs, "Plots")
        out_tab = oasysgui.createTabPage(main_tabs, "Output")
        script_tab = oasysgui.createTabPage(main_tabs, "Script")

        self.shadow4_script = PythonScript()
        self.shadow4_script.code_area.setFixedHeight(400)

        script_box = gui.widgetBox(script_tab, "Python script", addSpace=True, orientation="horizontal")
        script_box.layout().addWidget(self.shadow4_script)

        view_box = oasysgui.widgetBox(plot_tab, "", addSpace=False, orientation="horizontal")
        view_box_1 = oasysgui.widgetBox(view_box, "", addSpace=False, orientation="vertical", width=350)

        self.view_type_combo = gui.comboBox(view_box_1, self, "view_type", label="Plot Results",
                                            labelWidth=220,
                                            items=["No", "Yes"],
                                            callback=self.set_plot_quality, sendSelectedValue=False, orientation="horizontal")

        self.tabs = oasysgui.tabWidget(plot_tab)

        self.tabs_setting = oasysgui.tabWidget(self.controlArea)
        self.tabs_setting.setFixedHeight(self.TABS_AREA_HEIGHT)
        self.tabs_setting.setFixedWidth(self.CONTROL_AREA_WIDTH-5)

        tab_bas = oasysgui.createTabPage(self.tabs_setting, "General")

        box_1 = oasysgui.widgetBox(tab_bas, "Calculation Parameters", addSpace=True, orientation="vertical", height=130)

        gui.comboBox(box_1, self, "calculation_type", label="Calculation", labelWidth=70,
                     items=["Diffraction by Simple Aperture",
                            "Diffraction by Mirror or Grating Size",
                            "Diffraction by Mirror Size + Figure Errors",
                            "Diffraction by Grating Size + Figure Errors",
                            "Diffraction by Lens Size",
                            "Diffraction by Lens Size + Thickness Errors",
                            "Diffraction by KB Size",
                            "Diffraction by KB Size + Figure Errors"],
                     callback=self.set_calculation_type,
                     sendSelectedValue=False, orientation="horizontal")

        self.cb_diffraction_plane = gui.comboBox(box_1, self, "diffraction_plane", label="Diffraction Plane", labelWidth=310,
                                                 items=["Sagittal", "Tangential", "Both (2D)", "Both (1D+1D)"],
                                                 callback=self.set_diffraction_plane,
                                                 sendSelectedValue=False, orientation="horizontal")

        self.cb_propagation_type = gui.comboBox(box_1, self, "propagation_type", label="Propagation Type", labelWidth=310,
                                                items=["Far Field", "Near Field", "Both"],
                                                sendSelectedValue=False, orientation="horizontal", callback=self.set_propagation_type)

        gui.separator(box_1)

        box_2 = oasysgui.widgetBox(tab_bas, "Numerical Control Parameters", addSpace=True, orientation="vertical", height=140)

        self.le_n_bins_x = oasysgui.lineEdit(box_2, self, "n_bins_x", "Number of bins for I(Sagittal) histogram", labelWidth=260, valueType=int, orientation="horizontal")
        self.le_n_bins_z = oasysgui.lineEdit(box_2, self, "n_bins_z", "Number of bins for I(Tangential) histogram", labelWidth=260, valueType=int, orientation="horizontal")
        self.le_n_peaks   = oasysgui.lineEdit(box_2, self, "n_peaks", "Number of diffraction peaks", labelWidth=260, valueType=int, orientation="horizontal")
        self.le_fft_n_pts = oasysgui.lineEdit(box_2, self, "fft_n_pts", "Number of points for FFT", labelWidth=260, valueType=int, orientation="horizontal")

        box_4 = oasysgui.widgetBox(tab_bas, "Calculation Congruence Parameters", addSpace=True, orientation="vertical", height=100)

        self.cb_analyze_geometry = gui.comboBox(box_4, self, "analyze_geometry", label="Analize geometry to avoid unuseful calculations", labelWidth=310,
                                                items=["No", "Yes"],
                                                sendSelectedValue=False, orientation="horizontal")


        gui.comboBox(box_4, self, "send_original_beam", label="Send Original Beam in case of failure", labelWidth=310,
                     items=["No", "Yes"],
                     sendSelectedValue=False, orientation="horizontal")

        self.set_calculation_type()

        self.initializeTabs()

        self.shadow_output = oasysgui.textArea(height=580, width=800)

        out_box = gui.widgetBox(out_tab, "System Output", addSpace=True, orientation="horizontal")
        out_box.layout().addWidget(self.shadow_output)

        self.set_plot_quality()

    def set_plot_quality(self):
        self.progressBarInit()

        if not self.__plotted_data is None:
            try:
                self.initializeTabs()
                if self.view_type == 1: self.plot_results(self.__plotted_data)

            except Exception as exception:
                QMessageBox.critical(self, "Error", str(exception), QMessageBox.Ok)

                if self.IS_DEVELOP: raise exception
        else:
            self.initializeTabs()

        self.progressBarFinished()

    def initializeTabs(self):
        self.tabs.clear()

        if self.diffraction_plane in [HybridDiffractionPlane.SAGITTAL, HybridDiffractionPlane.TANGENTIAL]:
            if self.propagation_type == HybridPropagationType.BOTH:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field)"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field)"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Far Field)"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Near Field)")]
            elif self.propagation_type == HybridPropagationType.FAR_FIELD:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field)"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Far Field)")]
            elif self.propagation_type == HybridPropagationType.NEAR_FIELD:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field)"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Near Field)")]
        elif self.diffraction_plane == HybridDiffractionPlane.BOTH_2D:
             self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field)"),
                         gui.createTabPage(self.tabs, "Spatial Distribution (Far Field)")]
        elif self.diffraction_plane == HybridDiffractionPlane.BOTH_2X1D:
            if self.propagation_type == HybridPropagationType.BOTH:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field (S))"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field (S))"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field (T))"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field (T))"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Far Field)"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Near Field)")]
            elif self.propagation_type == HybridPropagationType.FAR_FIELD:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field (S))"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Divergence (Far Field (T))"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Far Field)")]
            elif self.propagation_type == HybridPropagationType.NEAR_FIELD:
                self.tab = [gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field (S))"),
                            gui.createTabPage(self.tabs, u"\u2206" + "Position (Near Field (T))"),
                            gui.createTabPage(self.tabs, "Spatial Distribution (Near Field)")]

        for tab in self.tab:
            tab.setFixedHeight(self.IMAGE_HEIGHT)
            tab.setFixedWidth(self.IMAGE_WIDTH)

        self.plot_canvas = [None, None, None, None, None, None]

    def plot_xy(self, beam_out, progressBarValue, var_x, var_y, plot_canvas_index, title, xtitle, ytitle, xum="", yum=""):
        if self.plot_canvas[plot_canvas_index] is None:
            self.plot_canvas[plot_canvas_index] = ShadowPlot.DetailedPlotWidget()
            self.tab[plot_canvas_index].layout().addWidget(self.plot_canvas[plot_canvas_index])

        self.plot_canvas[plot_canvas_index].plot_xy(beam_out, var_x, var_y, title, xtitle, ytitle, xum=xum, yum=yum)

        self.progressBarSet(progressBarValue)

    def plot_histo(self, beam_out, progressBarValue, var, plot_canvas_index, title, xtitle, ytitle, xum=""):
        if self.plot_canvas[plot_canvas_index] is None:
            self.plot_canvas[plot_canvas_index] = ShadowPlot.DetailedHistoWidget()
            self.tab[plot_canvas_index].layout().addWidget(self.plot_canvas[plot_canvas_index])

        self.plot_canvas[plot_canvas_index].plot_histo(beam_out, var, 1, None, 23, title, xtitle, ytitle, xum=xum)

        self.progressBarSet(progressBarValue)

    def plot_emtpy(self, progressBarValue, plot_canvas_index):
        if self.plot_canvas[plot_canvas_index] is None:
            widget = QWidget()
            widget.setLayout(QHBoxLayout())
            label = QLabel(widget)
            label.setPixmap(QPixmap(QImage(os.path.join(resources.package_dirname("orangecontrib.shadow.widgets.special_elements"), "../../../../../OASYS1-SHADOW4/orangecontrib/shadow4/widgets/optics/icons", "no_result.png"))))

            widget.layout().addWidget(label)

            self.plot_canvas[plot_canvas_index] = widget

            self.tab[plot_canvas_index].layout().addWidget(self.plot_canvas[plot_canvas_index])

        self.progressBarSet(progressBarValue)

    def plot_xy_hybrid(self, progressBarValue, scaled_matrix, plot_canvas_index, title, xtitle, ytitle, var1, var2):
        if self.plot_canvas[plot_canvas_index] is None:
            self.plot_canvas[plot_canvas_index] = ImageView()
            self.plot_canvas[plot_canvas_index].setColormap({"name":"temperature", "normalization":"linear", "autoscale":True, "vmin":0, "vmax":0, "colors":256})
            self.plot_canvas[plot_canvas_index].setMinimumWidth(590)
            self.plot_canvas[plot_canvas_index].setMaximumWidth(590)

            self.tab[plot_canvas_index].layout().addWidget(self.plot_canvas[plot_canvas_index])

        factor1 = ShadowPlot.get_factor(var1) # results are in m
        factor2 = ShadowPlot.get_factor(var2) # results are in m

        xmin, xmax = min(scaled_matrix.x_coord), max(scaled_matrix.x_coord)
        ymin, ymax = min(scaled_matrix.y_coord), max(scaled_matrix.y_coord)

        dim_x, dim_y = scaled_matrix.shape()

        origin = (xmin*factor1, ymin*factor2)
        scale = (abs((xmax-xmin)/dim_x)*factor1, abs((ymax-ymin)/dim_y)*factor2)

        data_to_plot = []
        for y_index in range(0, dim_y):
            x_values = []
            for x_index in range(0, dim_x):
                x_values.append(scaled_matrix.z_values[x_index, y_index])

            data_to_plot.append(x_values)

        self.plot_canvas[plot_canvas_index].setImage(numpy.array(data_to_plot), origin=origin, scale=scale)
        self.plot_canvas[plot_canvas_index].setGraphXLabel(xtitle)
        self.plot_canvas[plot_canvas_index].setGraphYLabel(ytitle)
        self.plot_canvas[plot_canvas_index].setGraphTitle(title)

        self.progressBarSet(progressBarValue)

    def plot_histo_hybrid(self, progressBarValue, scaled_array, plot_canvas_index, title, xtitle, ytitle, var):
        if self.plot_canvas[plot_canvas_index] is None:
            self.plot_canvas[plot_canvas_index] = oasysgui.plotWindow(roi=False, control=False, position=True)
            self.plot_canvas[plot_canvas_index].setDefaultPlotLines(True)
            self.plot_canvas[plot_canvas_index].setActiveCurveStyle(color='blue')
            self.plot_canvas[plot_canvas_index].setInteractiveMode(mode='zoom')

            self.tab[plot_canvas_index].layout().addWidget(self.plot_canvas[plot_canvas_index])

        factor = ShadowPlot.get_factor(var) # results are in m

        self.plot_canvas[plot_canvas_index].addCurve(scaled_array.scale*factor, scaled_array.np_array, "crv_"+ytitle, symbol='', color="blue", replace=True) #'+', '^', ','
        self.plot_canvas[plot_canvas_index]._backend.ax.get_yaxis().get_major_formatter().set_useOffset(True)
        self.plot_canvas[plot_canvas_index]._backend.ax.get_yaxis().get_major_formatter().set_scientific(True)
        self.plot_canvas[plot_canvas_index].setGraphXLabel(xtitle)
        self.plot_canvas[plot_canvas_index].setGraphYLabel(ytitle)
        self.plot_canvas[plot_canvas_index].setGraphTitle(title)
        self.plot_canvas[plot_canvas_index].replot()

        self.progressBarSet(progressBarValue)

    @Inputs.shadow_data
    def set_shadow_data(self, input_data: ShadowData):
        if ShadowCongruence.check_empty_data(input_data):
            if ShadowCongruence.check_empty_beam(input_data.beam):
                self.__input_data = input_data.duplicate()

                if self.is_automatic_run: self.run_hybrid()

    def set_calculation_type(self):
        self.cb_diffraction_plane.setEnabled(True)
        self.cb_propagation_type.setEnabled(True)
        self.cb_analyze_geometry.setEnabled(True)

        if self.tabs_setting.count() == 2: self.tabs_setting.removeTab(1)

        if self.calculation_type == HybridCalculationType.SIMPLE_APERTURE:
            self.cb_propagation_type.setEnabled(False)
            self.propagation_type = HybridPropagationType.FAR_FIELD
        elif self.calculation_type in [HybridCalculationType.CRL_SIZE, HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE]:
            self.diffraction_plane = HybridDiffractionPlane.BOTH_2D
            self.propagation_type = HybridPropagationType.FAR_FIELD
            self.cb_diffraction_plane.setEnabled(False)
            self.cb_propagation_type.setEnabled(False)
            if self.calculation_type == HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE: self.create_tab_thickness()
        elif self.calculation_type in [HybridCalculationType.KB_SIZE, HybridCalculationType.KB_SIZE_AND_ERROR_PROFILE]:
            self.diffraction_plane = HybridDiffractionPlane.BOTH_2X1D
            self.cb_diffraction_plane.setEnabled(False)

        if self.diffraction_plane == HybridDiffractionPlane.BOTH_2D:
            self.cb_propagation_type.setEnabled(False)
            self.propagation_type = HybridPropagationType.FAR_FIELD

        if self.calculation_type in [HybridCalculationType.MIRROR_SIZE_AND_ERROR_PROFILE,
                                     HybridCalculationType.GRATING_SIZE_AND_ERROR_PROFILE,
                                     HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE,
                                     HybridCalculationType.KB_SIZE_AND_ERROR_PROFILE]:
            self.analyze_geometry = 0
            self.cb_analyze_geometry.setEnabled(False)

        self.set_diffraction_plane()
        self.set_propagation_type()

    def set_diffraction_plane(self):
        self.le_n_bins_x.setEnabled(self.diffraction_plane in [HybridDiffractionPlane.SAGITTAL, HybridDiffractionPlane.BOTH_2X1D, HybridDiffractionPlane.BOTH_2D])
        self.le_n_bins_z.setEnabled(self.diffraction_plane in [HybridDiffractionPlane.TANGENTIAL, HybridDiffractionPlane.BOTH_2X1D, HybridDiffractionPlane.BOTH_2D])

    def set_propagation_type(self): pass # maybe it can be useful again


    # --------------------------------------------------
    # HybridListener methods
    # --------------------------------------------------

    def status_message(self, message : str): self.setStatusMessage(message)
    def set_progress_value(self, value):
        if value >= 100: self.progressBarFinished()
        elif value <= 0: self.progressBarInit()
        else:            self.progressBarSet(value)
    def warning_message(self, message : str): QMessageBox.warning(self, "Warning", str(message), QMessageBox.Ok)
    def error_message(self, message : str):   QMessageBox.critical(self, "Error", str(message), QMessageBox.Ok)

    def run_hybrid(self):
        try:
            self.setStatusMessage("")
            self.progressBarInit()
            self.initializeTabs()

            if ShadowCongruence.check_empty_data(self.__input_data):
                if ShadowCongruence.check_empty_beam(self.__input_data.beam):
                    sys.stdout = EmittingStream(textWritten=self.write_stdout)

                    self.check_fields()

                    beam     = self.__input_data.beam
                    beamline = self.__input_data.beamline

                    additional_parameters = {}

                    if self.calculation_type == HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE:
                        additional_parameters["crl_error_profiles"] = self.crl_error_profiles
                        if self.crl_material_data == 0: additional_parameters["crl_material"] = self.crl_material
                        else:                           additional_parameters["crl_delta"]    = self.crl_delta
                        additional_parameters["crl_scaling_factor"] = self.crl_scaling_factor
                        additional_parameters["crl_coords_to_m"]    = self.crl_coords_to_m
                        additional_parameters["crl_thickness_to_m"] = self.crl_thickness_to_m

                    if self.calculation_type in [HybridCalculationType.KB_SIZE, HybridCalculationType.KB_SIZE_AND_ERROR_PROFILE]:
                        if beamline.get_beamline_elements_number() < 2: raise ValueError("Calculation with KB not possible: not enough elements in the beamline")

                        kb_mirror_1_element = beamline.get_beamline_element_at(-2)
                        kb_mirror_2_element = beamline.get_beamline_element_at(-1)
                        beam             = S4HybridBeam(beam=[kb_mirror_2_element.get_input_beam(), beam])
                        optical_element  = S4HybridOE(optical_element=[kb_mirror_1_element, kb_mirror_2_element])
                    else:
                        beamline_element = beamline.get_beamline_element_at(-1)
                        beam             = S4HybridBeam(beam=beam)
                        optical_element  = S4HybridOE(optical_element=beamline_element)

                    input_parameters = HybridInputParameters(listener=self,
                                                             beam=beam,
                                                             optical_element=optical_element,
                                                             diffraction_plane=self.diffraction_plane,
                                                             propagation_type=self.propagation_type,
                                                             n_bins_x=int(self.n_bins_x),
                                                             n_bins_z=int(self.n_bins_z),
                                                             n_peaks=int(self.n_peaks),
                                                             fft_n_pts=int(self.fft_n_pts),
                                                             analyze_geometry=self.analyze_geometry==1,
                                                             random_seed=None,  # TODO: add field
                                                             **additional_parameters)
                    try:

                        beamline = self.__input_data.beamline.duplicate()
                        element = S4HybridScreenElement(hybrid_screen=S4HybridScreen(calculation_type=self.calculation_type),
                                                        hybrid_input_parameters=input_parameters)

                        beamline.append_beamline_element(element)

                        #
                        # script
                        #
                        script = beamline.to_python_code()
                        script += "\n\n\n# test plot"
                        script += "\nif True:"
                        script += "\n   from srxraylib.plot.gol import plot_scatter"
                        script += "\n   plot_scatter(beam.get_photon_energy_eV(nolost=1), beam.get_column(23, nolost=1), title='(Intensity,Photon Energy)', plot_histograms=0)"
                        script += "\n   plot_scatter(1e6 * beam.get_column(1, nolost=1), 1e6 * beam.get_column(3, nolost=1), title='(X,Z) in microns')"
                        self.shadow4_script.set_code(script)

                        self.progressBarInit()
                        beam, mirr, calculation_result = element.trace_beam()

                        # PARAMETERS IN SET MODE
                        self.n_bins_x                                   = int(input_parameters.n_bins_x)
                        self.n_bins_z                                   = int(input_parameters.n_bins_z)
                        self.n_peaks                                    = int(input_parameters.n_peaks)
                        self.fft_n_pts                                  = int(input_parameters.fft_n_pts)

                        self.__plotted_data = calculation_result
                        if self.view_type==1: self.plot_results(beam, calculation_result)

                        if self.propagation_type == HybridPropagationType.BOTH:
                            shadow_data_ff = ShadowData(beam=beam[0], beamline=beamline)
                            shadow_data_ff.scanning_data = self.__input_data.scanning_data
                            shadow_data_ff.initial_flux  = self.__input_data.initial_flux
                            shadow_data_nf = ShadowData(beam=beam[1], beamline=beamline)
                            shadow_data_nf.scanning_data = self.__input_data.scanning_data
                            shadow_data_nf.initial_flux  = self.__input_data.initial_flux
                            self.Outputs.shadow_data_ff.send(shadow_data_ff)
                            self.Outputs.shadow_data_nf.send(shadow_data_nf)
                        elif self.propagation_type == HybridPropagationType.FAR_FIELD:
                            shadow_data_ff = ShadowData(beam=beam, beamline=beamline)
                            shadow_data_ff.scanning_data = self.__input_data.scanning_data
                            shadow_data_ff.initial_flux  = self.__input_data.initial_flux
                            self.Outputs.shadow_data_ff.send(shadow_data_ff)
                        elif self.propagation_type == HybridPropagationType.NEAR_FIELD:
                            shadow_data_nf = ShadowData(beam=beam, beamline=beamline)
                            shadow_data_nf.scanning_data = self.__input_data.scanning_data
                            shadow_data_nf.initial_flux  = self.__input_data.initial_flux
                            self.Outputs.shadow_data_nf.send(shadow_data_nf)

                        self.Outputs.trigger.send(TriggerIn(new_object=True))
                    except Exception as e:
                        if self.send_original_beam==1:
                            self.Outputs.shadow_data_ff.send(self.__input_data.duplicate())
                            self.Outputs.trigger.send(TriggerIn(new_object=True))
                        else:
                            raise e
                else:
                    raise Exception("Empty Input Beam")
            else:
                raise Exception("Empty Input Data")
        except Exception as exception:
            QMessageBox.critical(self, "Error", str(exception), QMessageBox.Ok)

            if self.IS_DEVELOP: raise exception

        self.setStatusMessage("")
        self.progressBarFinished()

    def plot_results(self, beam, calculation_result : HybridCalculationResult):
        geometry_analysis = calculation_result.geometry_analysis

        do_plot_sagittal   = True
        do_plot_tangential = True

        if not self.calculation_type in [HybridCalculationType.MIRROR_SIZE_AND_ERROR_PROFILE,
                                         HybridCalculationType.GRATING_SIZE_AND_ERROR_PROFILE,
                                         HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE] \
                and self.analyze_geometry == 1:
            do_plot_sagittal   = not geometry_analysis.has_result(HybridGeometryAnalysis.BEAM_NOT_CUT_SAGITTALLY)
            do_plot_tangential = not geometry_analysis.has_result(HybridGeometryAnalysis.BEAM_NOT_CUT_TANGENTIALLY)

        if do_plot_sagittal or do_plot_tangential: self.setStatusMessage("Plotting Results")
        
        def plot_direction(direction="S", do_plot=True, progress=[84, 88, 92, 96], start_index=0, plot_shadow=True):
            if direction=="S":
                ax         = "X"
                divergence = calculation_result.divergence_sagittal
                position   = calculation_result.position_sagittal
                var        = 1
            else:
                ax         = "Z"
                divergence = calculation_result.divergence_tangential
                position   = calculation_result.position_tangential
                var        = 3

            if do_plot:
                if self.propagation_type == HybridPropagationType.BOTH:
                    self.plot_histo_hybrid(progress[0], divergence, start_index + 0,     title=u"\u2206" + ax + "p", xtitle=r'$\Delta$' + ax + 'p [$\\mu$rad]', ytitle='Arbitrary Units', var=4)
                    self.plot_histo_hybrid(progress[2], position,   start_index + 1, title=u"\u2206" + ax, xtitle=r'$\Delta$' + ax + ' [$\\mu$m]', ytitle='Arbitrary Units', var=1)
                    if plot_shadow:
                        self.plot_histo(beam[0], progress[1], var, plot_canvas_index=start_index + 2, title=ax, xtitle=r'' + ax + ' [$\\mu$m]', ytitle=r'Number of Rays', xum=("X [" + u"\u03BC" + "m]"))
                        self.plot_histo(beam[1], progress[3], var, plot_canvas_index=start_index + 3, title=ax, xtitle=r'' + ax + ' [$\\mu$m]', ytitle=r'Number of Rays', xum=("X [" + u"\u03BC" + "m]"))
                elif self.propagation_type == HybridPropagationType.FAR_FIELD:
                    self.plot_histo_hybrid(progress[1], divergence, start_index + 0, title=u"\u2206" + ax + "p", xtitle=r'$\Delta$' + ax + 'p [$\\mu$rad]', ytitle=r'Arbitrary Units', var=4)
                    if plot_shadow:
                        self.plot_histo(beam, progress[3], var, plot_canvas_index=start_index + 1, title=ax, xtitle=r'' + ax + ' [$\\mu$m]', ytitle=r'Number of Rays', xum=("X [" + u"\u03BC" + "m]"))
                elif self.propagation_type == HybridPropagationType.NEAR_FIELD:
                    self.plot_histo_hybrid(progress[1], position, start_index + 0, title=u"\u2206" + ax, xtitle=r'$\Delta$' + ax + ' [$\\mu$m]', ytitle=r'Arbitrary Units', var=1)
                    if plot_shadow:
                        self.plot_histo(beam, progress[3], var, plot_canvas_index=start_index + 1, title=ax, xtitle=r'' + ax + ' [$\\mu$m]', ytitle=r'Number of Rays', xum=("X [" + u"\u03BC" + "m]"))
            else:
                if self.propagation_type == HybridPropagationType.BOTH:
                    self.plot_emtpy(progress[0], start_index + 0)
                    self.plot_emtpy(progress[1], start_index + 1)
                    if plot_shadow:
                        self.plot_emtpy(progress[2], start_index + 2)
                        self.plot_emtpy(progress[3], start_index + 3)
                else:
                    self.plot_emtpy(progress[1], start_index + 0)
                    if plot_shadow:
                        self.plot_emtpy(progress[3], start_index + 1)

        if   self.diffraction_plane == HybridDiffractionPlane.SAGITTAL:   plot_direction("S", do_plot_sagittal)
        elif self.diffraction_plane == HybridDiffractionPlane.TANGENTIAL: plot_direction("T", do_plot_tangential)
        elif self.diffraction_plane == HybridDiffractionPlane.BOTH_2D:
            if do_plot_sagittal and do_plot_tangential:
                self.plot_xy_hybrid(88, calculation_result.divergence_2D, plot_canvas_index=0, title="X',Z'",xtitle="X' [$\\mu$rad]", ytitle="Z' [$\\mu$rad]", var1=4, var2=6)
                self.plot_xy(beam, 96, 1, 3, plot_canvas_index=1, title="X,Z", xtitle=r'X [$\mu$m]', ytitle=r'Z [$\mu$m]', xum=("X [" + u"\u03BC" + "m]"), yum=("Z [" + u"\u03BC" + "m]"))
            else:
                if do_plot_sagittal:
                    self.plot_histo_hybrid(88, calculation_result.divergence_sagittal, plot_canvas_index=0, title=u"\u2206" + "X'", xtitle="$\\Delta$X' [$\\mu$rad]", ytitle=r'Arbitrary Units', var=4)
                    self.plot_histo(beam, 96, 1, plot_canvas_index=1, title="X", xtitle=r'X [$\mu$m]', ytitle=r'Number of Rays', xum=("X [" + u"\u03BC" + "m]"))
                elif do_plot_tangential:
                    self.plot_histo_hybrid(88, calculation_result.divergence_tangential, 0, title=u"\u2206" + "Z'", xtitle="$\\Delta$Z' [$\\mu$rad]", ytitle=r'Arbitrary Units', var=6)
                    self.plot_histo(beam, 96, 3, plot_canvas_index=1, title="Z", xtitle=r'Z [$\mu$m]', ytitle=r'Number of Rays', xum=("Z [" + u"\u03BC" + "m]"))
                else:
                    self.plot_emtpy(88, 0)
                    self.plot_emtpy(96, 1)
        elif self.diffraction_plane == HybridDiffractionPlane.BOTH_2X1D:
            plot_direction("S", do_plot_sagittal,   progress=[82, 82, 84, 84], plot_shadow=False)
            plot_direction("T", do_plot_tangential, progress=[86, 88, 94, 98], start_index=2 if self.propagation_type == HybridPropagationType.BOTH else 1, plot_shadow=False)

            if self.propagation_type == HybridPropagationType.BOTH:
                self.plot_xy(beam[0], 94, 1, 3, plot_canvas_index=4 if self.propagation_type == HybridPropagationType.BOTH else 2,
                             title="X,Z", xtitle=r'X [$\mu$m]', ytitle=r'Z [$\mu$m]', xum=("X [" + u"\u03BC" + "m]"), yum=("Z [" + u"\u03BC" + "m]"))
                self.plot_xy(beam[1], 94, 1, 3, plot_canvas_index=5 if self.propagation_type == HybridPropagationType.BOTH else 3,
                             title="X,Z", xtitle=r'X [$\mu$m]', ytitle=r'Z [$\mu$m]', xum=("X [" + u"\u03BC" + "m]"), yum=("Z [" + u"\u03BC" + "m]"))
            else:
                self.plot_xy(beam, 94, 1, 3, plot_canvas_index=2,
                             title="X,Z", xtitle=r'X [$\mu$m]', ytitle=r'Z [$\mu$m]', xum=("X [" + u"\u03BC" + "m]"), yum=("Z [" + u"\u03BC" + "m]"))

    def check_fields(self):
        if self.diffraction_plane in [HybridDiffractionPlane.SAGITTAL, HybridDiffractionPlane.BOTH_2D]:
            congruence.checkStrictlyPositiveNumber(self.n_bins_x, "Number of bins for I(Sagittal) histogram")
        if self.diffraction_plane in [HybridDiffractionPlane.TANGENTIAL, HybridDiffractionPlane.BOTH_2D]:
            congruence.checkStrictlyPositiveNumber(self.n_bins_z, "Number of bins for I(Tangential) histogram")

        congruence.checkStrictlyPositiveNumber(self.n_peaks, "Number of diffraction peaks")
        congruence.checkStrictlyPositiveNumber(self.fft_n_pts, "Number of points for FFT")

        if self.calculation_type == HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE:
            if len(self.crl_error_profiles) == 0: raise ValueError("No Thickness error profile specified")
            if self.crl_material_data == 0: self.crl_material = congruence.checkEmptyString(self.crl_material, "Chemical Formula")
            else: congruence.checkStrictlyPositiveNumber(self.crl_delta, "Refractive Index (\u03b4)")
            congruence.checkPositiveNumber(self.crl_scaling_factor, "Thickness Error Scaling Factor")


    def write_stdout(self, text):
        cursor = self.shadow_output.textCursor()
        cursor.movePosition(QTextCursor.End)
        cursor.insertText(text)
        self.shadow_output.setTextCursor(cursor)
        self.shadow_output.ensureCursorVisible()

    @Inputs.trigger
    def set_trigger_parameters_for_optics(self, trigger):
        super(HybridScreen, self).set_trigger_parameters_for_optics(trigger)

    @Inputs.thickness_error_data
    def set_thickness_error_profiles(self, thickness_errors_data):
        try:
            thickness_error_profile_data_files = thickness_errors_data.thickness_error_profile_data_files

            if not thickness_error_profile_data_files is None:
                self.crl_error_profiles = [thickness_error_file for thickness_error_file in thickness_error_profile_data_files]
                if self.calculation_type==HybridCalculationType.CRL_SIZE_AND_ERROR_PROFILE: self.refresh_files_text_area()
        except Exception as exception:
            QMessageBox.critical(self, "Error", exception.args[0], QMessageBox.Ok)

            if self.IS_DEVELOP: raise exception

    def refresh_files_text_area(self):
        text = ""
        for file in self.crl_error_profiles: text += file + "\n"

        self.files_area.setText(text)

    def create_tab_thickness(self):
        tab_thick = oasysgui.createTabPage(self.tabs_setting, "Thickness Error")

        input_box = oasysgui.widgetBox(tab_thick, "Thickness Error Files", addSpace=True, orientation="vertical", height=450, width=self.CONTROL_AREA_WIDTH-20)

        gui.comboBox(input_box, self, "crl_material_data", label="Material Properties from", labelWidth=180,
                     items=["Chemical Formula", "Absorption Parameters"],
                     callback=self.set_crl_material_data,
                     sendSelectedValue=False, orientation="horizontal")

        self.input_box_1 = oasysgui.widgetBox(input_box, "", addSpace=False, orientation="vertical", width=self.CONTROL_AREA_WIDTH-40)
        self.input_box_2 = oasysgui.widgetBox(input_box, "", addSpace=False, orientation="vertical", width=self.CONTROL_AREA_WIDTH-40)

        oasysgui.lineEdit(self.input_box_1, self, "crl_material", "Chemical Formula", labelWidth=260, valueType=str, orientation="horizontal")
        oasysgui.lineEdit(self.input_box_2, self, "crl_delta", "Refractive Index (\u03b4)", labelWidth=260, valueType=float, orientation="horizontal")

        self.set_crl_material_data()

        self.files_area = oasysgui.textArea(height=265)

        input_box.layout().addWidget(self.files_area)

        self.refresh_files_text_area()

        oasysgui.lineEdit(input_box, self, "crl_thickness_to_m", label="Thickness conversion to m", labelWidth=260, orientation="horizontal", valueType=float)
        oasysgui.lineEdit(input_box, self, "crl_coords_to_m",   label="Coordinates conversion to m", labelWidth=260, orientation="horizontal", valueType=float)
        oasysgui.lineEdit(input_box, self, "crl_scaling_factor", "Thickness Error Scaling Factor", labelWidth=260, valueType=float, orientation="horizontal")

    def set_crl_material_data(self):
        self.input_box_1.setVisible(self.crl_material_data==0)
        self.input_box_2.setVisible(self.crl_material_data==1)

add_widget_parameters_to_module(__name__)