from soma.qt_gui.controls.File import FileControlWidget
from soma.qt_gui.controls.Directory import DirectoryControlWidget
from soma.qt_gui.controls.List_File_offscreen \
    import OffscreenListFileControlWidget
from soma.qt_gui import controller_widget
from soma.qt_gui.qt_backend import Qt, QtGui, QtWidgets
from functools import partial
from soma.utils.weak_proxy import weak_proxy
import traits.api as traits
import six

# Define the logger
import logging
logger = logging.getLogger(__name__)

class PopulseFileControlWidget(FileControlWidget):

    @staticmethod
    def create_widget(parent, control_name, control_value, trait,
                      label_class=None, user_data=None):
        """ Method to create the file widget.

        Parameters
        ----------
        parent: QWidget (mandatory)
            the parent widget
        control_name: str (mandatory)
            the name of the control we want to create
        control_value: str (mandatory)
            the default control value
        trait: Tait (mandatory)
            the trait associated to the control
        label_class: Qt widget class (optional, default: None)
            the label widget will be an instance of this class. Its constructor
            will be called using 2 arguments: the label string and the parent
            widget.

        Returns
        -------
        out: 2-uplet
            a two element tuple of the form (control widget: QWidget with two
            elements, a QLineEdit in the 'path' parameter and a browse button
            in the 'browse' parameter, associated label: QLabel)
        """
        # Create the widget that will be used to select a file
        widget, label = FileControlWidget.create_widget(
            parent, control_name, control_value, trait,
            label_class=label_class, user_data=user_data)
        if user_data is None:
            user_data = {}
        widget.user_data = user_data  # regular File does not store data

        layout = widget.layout()

        project = user_data.get('project')
        scan_list = user_data.get('scan_list')
        connected_inputs = user_data.get('connected_inputs', set())

        def is_number(x):
            try:
                int(x)
                return True
            except ValueError:
                return False

        main_window = user_data.get('main_window')
        # files in a list don't get a Filter button.
        if project and scan_list and not trait.output \
                and control_name not in connected_inputs \
                and not is_number(control_name):
            # Create a browse button
            button = Qt.QPushButton("Filter", widget)
            button.setObjectName('filter_button')
            button.setStyleSheet('QPushButton#filter_button '
                                '{padding: 2px 10px 2px 10px; margin: 0px;}')
            layout.addWidget(button)
            widget.filter_b = button

            # Set a callback on the browse button
            control_class = parent.get_control_class(trait)
            node_name = getattr(parent.controller, 'name', None)
            if node_name is None:
                node_name = parent.controller.__class__.__name__
            browse_hook = partial(
                control_class.filter_clicked, weak_proxy(widget), node_name,
                control_name)
            widget.filter_b.clicked.connect(browse_hook)

        return (widget, label)

    @staticmethod
    def filter_clicked(widget, node_name, plug_name):
        """Display a filter widget.

        :param node_name: name of the node
        :param plug_name: name of the plug
        """
        # this import is not at the beginning of the file to avoid a cyclic
        # import issue.
        from populse_mia.user_interface.pipeline_manager.node_controller \
            import PlugFilter

        project = widget.user_data.get('project')
        scan_list = widget.user_data.get('scan_list')
        main_window = widget.user_data.get('main_window')
        node_controller = widget.user_data.get('node_controller')
        widget.pop_up = PlugFilter(project, scan_list, None, #(process)
                                   node_name, plug_name, node_controller,
                                   main_window)
        widget.pop_up.setWindowModality(Qt.Qt.WindowModal)
        widget.pop_up.show()

        widget.pop_up.plug_value_changed.connect(
            partial(PopulseFileControlWidget.update_plug_value_from_filter,
                    widget, plug_name))

    @staticmethod
    def update_plug_value_from_filter(widget, plug_name, filter_res_list):
        """Update the plug value from a filter result.

        :param plug_name: name of the plug
        :param filter_res_list: list of the filtered files
        """
        # If the list contains only one element, setting
        # this element as the plug value
        len_list = len(filter_res_list)

        if len_list == 1:
            res = filter_res_list[0]

        else:
            res = traits.Undefined

            if len_list > 1:
                msg = QtWidgets.QMessageBox()
                msg.setText("The '{0}' parameter must by a filename, "
                            "but a value of {1} <class \'list\'> was "
                            "specified.".format(plug_name, filter_res_list))
                msg.setIcon(QtWidgets.QMessageBox.Warning)
                msg.setWindowTitle('TraitError')
                msg.exec_()
                res = traits.Undefined

        # Set the selected file path to the path sub control
        widget.path.set_value(six.text_type(res))


class PopulseDirectoryControlWidget(DirectoryControlWidget):

    @staticmethod
    def create_widget(parent, control_name, control_value, trait,
                      label_class=None, user_data=None):
        return PopulseFileControlWidget.create_widget(
            parent, control_name, control_value, trait,
            label_class=label_class, user_data=user_data)

    @staticmethod
    def filter_clicked(widget, node_name, plug_name):
        """Display a filter widget.

        :param node_name: name of the node
        :param plug_name: name of the plug
        """
        # this import is not at the beginning of the file to avoid a cyclic
        # import issue.
        from populse_mia.user_interface.pipeline_manager.node_controller \
            import PlugFilter

        project = widget.user_data.get('project')
        scan_list = widget.user_data.get('scan_list')
        main_window = widget.user_data.get('main_window')
        node_controller = widget.user_data.get('node_controller')
        widget.pop_up = PlugFilter(project, scan_list, None, #(process)
                                   node_name, plug_name, node_controller,
                                   main_window)
        widget.pop_up.show()
        widget.pop_up.plug_value_changed.connect(
            partial(
                PopulseDirectoryControlWidget.update_plug_value_from_filter,
                widget, plug_name))

    @staticmethod
    def update_plug_value_from_filter(widget, plug_name, filter_res_list):
        """Update the plug value from a filter result.

        :param plug_name: name of the plug
        :param filter_res_list: list of the filtered files
        """
        # If the list contains only one element, setting
        # this element as the plug value
        len_list = len(filter_res_list)
        if len_list >= 1:
            res = six.text_type(filter_res_list[0])
            if not os.path.isdir(res):
                res = os.path.dirname(res)
        else:
            res = traits.Undefined

        # Set the selected file path to the path sub control
        widget.path.setText(six.text_type(res))


class PopulseOffscreenListFileControlWidget(OffscreenListFileControlWidget):

    @staticmethod
    def create_widget(parent, control_name, control_value, trait,
                      label_class=None, user_data=None):
        """ Method to create the list widget.

        Parameters
        ----------
        parent: QWidget (mandatory)
            the parent widget
        control_name: str (mandatory)
            the name of the control we want to create
        control_value: list of items (mandatory)
            the default control value
        trait: Tait (mandatory)
            the trait associated to the control
        label_class: Qt widget class (optional, default: None)
            the label widget will be an instance of this class. Its constructor
            will be called using 2 arguments: the label string and the parent
            widget.

        Returns
        -------
        out: 2-uplet
            a two element tuple of the form (control widget: ,
            associated labels: (a label QLabel, the tools QWidget))
        """
        widget, label = OffscreenListFileControlWidget.create_widget(
            parent, control_name, control_value, trait,
            label_class=label_class, user_data=user_data)

        layout = widget.layout()

        project = user_data.get('project')
        scan_list = user_data.get('scan_list')
        main_window = user_data.get('main_window')
        connected_inputs = user_data.get('connected_inputs', set())
        if project and scan_list and not trait.output \
                and control_name not in connected_inputs:
            # Create a browse button
            button = Qt.QPushButton("Filter", widget)
            button.setObjectName('filter_button')
            button.setStyleSheet('QPushButton#filter_button '
                                '{padding: 2px 10px 2px 10px; margin: 0px;}')
            layout.addWidget(button)
            widget.filter_b = button

            # Set a callback on the browse button
            control_class = parent.get_control_class(trait)
            node_name = getattr(parent.controller, 'name', None)
            if node_name is None:
                node_name = parent.controller.__class__.__name__
            browse_hook = partial(
                control_class.filter_clicked, weak_proxy(widget), node_name,
                control_name)
                                    #parameters, process)
            widget.filter_b.clicked.connect(browse_hook)

        return (widget, label)

    @staticmethod
    def filter_clicked(widget, node_name, plug_name):
        """Display a filter widget.

        :param node_name: name of the node
        :param plug_name: name of the plug
        """
        # this import is not at the beginning of the file to avoid a cyclic
        # import issue.
        from populse_mia.user_interface.pipeline_manager.node_controller \
            import PlugFilter

        project = widget.user_data.get('project')
        scan_list = widget.user_data.get('scan_list')
        main_window = widget.user_data.get('main_window')
        node_controller = widget.user_data.get('node_controller')
        widget.pop_up = PlugFilter(project, scan_list, None, #(process)
                                   node_name, plug_name, node_controller,
                                   main_window)
        widget.pop_up.show()
        widget.pop_up.plug_value_changed.connect(
            partial(
                PopulseOffscreenListFileControlWidget.update_plug_value_from_filter,
                widget, plug_name))

    @staticmethod
    def update_plug_value_from_filter(widget, plug_name, filter_res_list):
        """Update the plug value from a filter result.

        :param plug_name: name of the plug
        :param filter_res_list: list of the filtered files
        """
        controller = widget.parent().controller

        try:
            setattr(controller, plug_name, filter_res_list)

        except Exception as e:
            print(e)


#controller_widget.ControllerWidget._defined_controls['File'] \
    #= PopulseFileControlWidget
#controller_widget.ControllerWidget._defined_controls['Directory'] \
    #= PopulseDirectoryControlWidget


class PopulseUndefinedControlWidget(object):
    """Control for Undefined value.
    """

    @staticmethod
    def is_valid(control_instance, *args, **kwargs):
        """ Method to check if the new control value is correct.

        Parameters
        ----------
        control_instance: QWidget (mandatory)
            the control widget we want to validate

        Returns
        -------
        out: bool
            True if the control value is Undefined,
            False otherwise
        """

        # Get the control current value
        control_text = control_instance.text()

        is_valid = False
        
        if control_text in ['<undefined>',
          '<style>background-color: gray; text-color: red;</style><undefined>']:
            is_valid = True

        return is_valid
        
    @classmethod
    def check(cls, control_instance):
        """ Check if a controller widget control is filled correctly.

        Parameters
        ----------
        cls: StrControlWidget (mandatory)
            a StrControlWidget control
        control_instance: QLineEdit (mandatory)
            the control widget we want to validate
        """

        pass
 
    @staticmethod
    def create_widget(parent, control_name, control_value, trait,
                      label_class=None, user_data=None):
        """ Method to create the Undefined widget.

        Parameters
        ----------
        parent: QWidget (mandatory)
            the parent widget
        control_name: str (mandatory)
            the name of the control we want to create
        control_value: str (mandatory)
            the default control value
        trait: Tait (mandatory)
            the trait associated to the control
        label_class: Qt widget class (optional, default: None)
            the label widget will be an instance of this class. Its constructor
            will be called using 2 arguments: the label string and the parent
            widget.

        Returns
        -------
        out: 2-uplet
            a two element tuple of the form (control widget: QLineEdit,
            associated label: QLabel)
        """

        # Create the widget 
        widget = Qt.QLabel(
                '<style>background-color: gray; text-color: red;</style>'
                + str(traits.Undefined), parent)

        # Create the label associated with the string widget
        control_label = control_name
        if label_class is None:
            label_class = QtGui.QLabel
        if control_label is not None:
            label = label_class(control_label, parent)
        else:
            label = None

        return (widget, label)

    @staticmethod
    def update_controller(controller_widget, control_name, control_instance,
                          reset_invalid_value, *args, **kwargs):

        """ Update one element of the controller.

        At the end the controller trait value with the name 'control_name'
        will match the controller widget user parameters defined in
        'control_instance'.

        Parameters
        ----------
        controller_widget: ControllerWidget (mandatory)
            a controller widget that contains the controller we want to update
        control_name: str(mandatory)
            the name of the controller widget control we want to synchronize
            with the controller
        control_instance: QLineEdit (mandatory)
            the instance of the controller widget control we want to
            synchronize with the controller
        """

        # Update the controller only if the control is valid
        if PopulseUndefinedControlWidget.is_valid(control_instance):

            # Define the control value
            new_trait_value = traits.Undefined
            setattr(controller_widget.controller, control_name,
                    new_trait_value)
            logger.debug(
                "'PopulseUndefinedControlWidget' associated controller trait "
                "'{0}' has been updated with value '{1}'.".format(
                                                 control_name, new_trait_value))
        elif reset_invalid_value:
            # invalid, reset GUI to older value
            old_trait_value = getattr(controller_widget.controller,
                                      control_name)
            control_instance.setText(old_trait_value)

    @staticmethod
    def update_controller_widget(controller_widget, control_name,
                                 control_instance):
        """ Update one element of the controller widget.

        At the end the controller widget user editable parameter with the
        name 'control_name' will match the controller trait value with the same
        name.

        Parameters
        ----------
        controller_widget: ControllerWidget (mandatory)
            a controller widget that contains the controller we want to update
        control_name: str(mandatory)
            the name of the controller widget control we want to synchronize
            with the controller
        control_instance: QLineEdit (mandatory)
            the instance of the controller widget control we want to
            synchronize with the controller
        """

        # Define the trait value
        new_controller_value = str(traits.Undefined)

        # Set the trait
        control_instance.setText(new_controller_value)
        logger.debug("'PopulseUndefinedControlWidget' has been updated "
                     "with value '{0}'.".format(new_controller_value))
        # Set the controller trait value
        PopulseUndefinedControlWidget.update_controller(controller_widget,
                                   control_name, control_instance,
                                   True)

    @classmethod
    def connect(cls, controller_widget, control_name, control_instance):
        """ Connect a 'Str' or 'String' controller trait and a
        'StrControlWidget' controller widget control.

        Parameters
        ----------
        cls: StrControlWidget (mandatory)
            a StrControlWidget control
        controller_widget: ControllerWidget (mandatory)
            a controller widget that contains the controller we want to update
        control_name: str (mandatory)
            the name of the controller widget control we want to synchronize
            with the controller
        control_instance: QLineEdit (mandatory)
            the instance of the controller widget control we want to
            synchronize with the controller

        """

        pass

    @staticmethod
    def disconnect(controller_widget, control_name, control_instance):
        """ Disconnect a 'Str' or 'String' controller trait and a
        'StrControlWidget' controller widget control.

        Parameters
        ----------
        controller_widget: ControllerWidget (mandatory)
            a controller widget that contains the controller we want to update
        control_name: str(mandatory)
            the name of the controller widget control we want to synchronize
            with the controller
        control_instance: QLineEdit (mandatory)
            the instance of the controller widget control we want to
            synchronize with the controller
        """

        pass

