"""
Collection of widgets common to multiple BioLuminate panels.
Copyright (c) Schrodinger, LLC. All rights reserved
"""
#- Imports -------------------------------------------------------------------
import os
import schrodinger.ui.qt.filedialog as filedialog
from schrodinger.Qt import QtCore
from schrodinger.Qt import QtGui
from schrodinger.Qt import QtWidgets
from schrodinger.ui.qt import layout
from schrodinger.ui.qt.standard.colors import LightModeColors
from schrodinger.ui.qt.swidgets import SpinnerIcon  # noqa: F401
from .actions import icons
from .actions.configs import IMPORT_STRUCTURES
from .actions.factory import Factory
#- Classes -------------------------------------------------------------------
[docs]class SequenceFileDialog(filedialog.FileDialog):
    """
    Custom class to handle opening files related to
    sequences.
    """
    CAPTION = 'Import Sequences'
    DEFAULT_FILTERS = ';;'.join([
        'FASTA (*.fasta *.fst *.fas *.seq)',
        'Maestro (*.mae *.maegz *.mae.gz )', 'PDB (*.pdb *.ent)',
        'SWISSPROT (*.sw *.sp *.swiss *.swissprot)', 'GCG (*.gcg *.msf)',
        'EMBL (*.embl *.emb)', 'PIR (*.pir)', 'All Files (*.*)'
    ])
    """
    The default MSV filters.
    """
    REFERENCE_FILTERS = ';;'.join([
        'Common (*.fasta *.fst *.fas *.seq *.mae *.maegz *.mae.gz *.pdb *.ent '
        '*.txt)', 'FASTA (*.fasta *.fst *.fas *.seq)', 'PDB (*.pdb *.ent)',
        'Maestro (*.mae *.maegz *.mae.gz )', 'All Files (*.*)'
    ])
    """
    Filters for reference sequences in homology model building.
    """
    STRUCTURE_FILTERS = ';;'.join([
        'Structure files (*.mae *.maegz *.mae.gz *.pdb *.ent)',
        'Maestro (*.mae *.maegz *.mae.gz )',
        'PDB (*.pdb *.ent)',
    ])
    """
    Filters that have structures associated with them.
    """
[docs]    def __init__(self, parent=None, add_options=True, **kwargs):
        caption = kwargs.get('caption', self.CAPTION)
        filters = kwargs.get('filter', self.DEFAULT_FILTERS)
        if not parent:
            super(SequenceFileDialog, self).__init__()
            self.setWindowTitle(caption)
            self.setNameFilter(filters)
        else:
            kwargs['caption'] = caption
            kwargs['filter'] = filters
            super(SequenceFileDialog, self).__init__(parent, **kwargs)
        self.setAcceptMode(self.AcceptOpen)
        self.setLabelText(self.Accept, 'Open')
        if add_options:
            self.addOptions() 
[docs]    def addOptions(self):
        """
        Adds three widgets on the bottom of the dialog window that allow
        users to optionally:
           - Align to query sequence
           - Replace matching sequences
           - Incorporate PDB files into Maestro
        """
        self.merge_cb = QtWidgets.QCheckBox("Align to query sequence")
        self.replace_cb = QtWidgets.QCheckBox("Replace matching sequences")
        self.incorporate_cb = QtWidgets.QCheckBox(
            "Incorporate PDB files into Maestro")
        grid = self.layout()
        row = grid.rowCount()
        grid.addWidget(self.merge_cb, row, 1)
        grid.addWidget(self.replace_cb, row + 1, 1)
        grid.addWidget(self.incorporate_cb, row + 2, 1) 
[docs]    def getOpenFileName(self, multiple=False):
        if multiple:
            self.setFileMode(self.ExistingFiles)
        elif multiple == False:
            self.setFileMode(self.ExistingFile)
        else:
            raise RuntimeError('The "multiple" arg must be True or False.')
        # If not cancelled
        if self.exec_():
            files = [os.path.normpath(str(x)) for x in self.selectedFiles()]
            if not multiple:
                files = files[0]
            return files
        return [] 
[docs]    @staticmethod
    def get_open_file_names(parent=None,
                            add_options=True,
                            multiple=True,
                            **kwargs):
        dialog = SequenceFileDialog(parent, add_options=add_options, **kwargs)
        return dialog.getOpenFileName(multiple) 
[docs]    @staticmethod
    def get_open_file_name(parent=None, add_options=True, **kwargs):
        dialog = SequenceFileDialog.get_open_file_names(parent=parent,
                                                        add_options=add_options,
                                                        multiple=False,
                                                        **kwargs)
        return dialog  
[docs]class NumericLineEdit(QtWidgets.QLineEdit):
    """
    A `QtWidgets.QLineEdit` with a builtin validator for floats or integers.
    """
[docs]    def __init__(self,
                 parent=None,
                 width=50,
                 validate_type='float',
                 minimum=None,
                 maximum=None):
        QtWidgets.QLineEdit.__init__(self, parent=parent)
        self.setMaximumSize(QtCore.QSize(width, 16777215))
        if not minimum:
            minimum = 0.0
        if not maximum:
            maximum = 100000000.0
        if validate_type == 'float':
            self.setValidator(QtGui.QDoubleValidator(minimum, maximum, 5, self))
        elif validate_type == 'int':
            self.setValidator(QtGui.QIntValidator(minimum, maximum, self))
        self.textChanged.connect(self.validate) 
[docs]    def validate(self):
        """
        Checks to see if the lineedit has acceptable input and changes the
        widget to indicate invalid input. Even with a validator set users can
        input invalid args so this helps out.
        """
        if not self.hasAcceptableInput() and self.text() != '':
            self.setStyleSheet(
                f'QLineEdit {{ border: 2px solid {LightModeColors.INVALID_STATE_BORDER}; }}'
            )
        else:
            self.setStyleSheet('QLineEdit {  }')  
[docs]class RowActionItem(QtWidgets.QWidget):
    """
    Custom widget to be used in a table cell. It will create a widget that
    contains multiple small push buttons with icons. This will end up looking
    like just a small icon in the cell. Multipe actions can be added to a
    single cell
    """
[docs]    def __init__(self, row):
        """
        :param row: The row associated with the cell. This can be anything
                    (i.e. integer index, row object, etc.) and is used to
                    track the row.
        :type  row: mixed
        """
        super(RowActionItem, self).__init__()
        self.row = row
        self.setObjectName('row_action_item')
        self._action_items = []
        """ A private variable that stores the list of actions for the cell """
        self.setStyleSheet(f"""
            QPushButton#cell_button {{
                background-color: {LightModeColors.STANDARD_BACKGROUND};
                border-width: 0px;
                border-radius: 3px;
                border-color: {LightModeColors.STANDARD_BORDER};
                padding-top: 3px;
	        padding-bottom: 3px;
            }}
            """) 
[docs]    def addActionItem(self, icon, tooltip, callback):
        """
        Add an action to the cell widget.
        :param icon: the icon for the action
        :type  icon: QtGui.QIcon
        :param callback: The callback for the action
        :type  callback: callable
        """
        button = QtWidgets.QPushButton(icon, '', self)
        button.setToolTip(tooltip)
        button.setObjectName('cell_button')
        button.clicked.connect(callback)
        self._action_items.append(button) 
[docs]    def paintEvent(self, *args, **kwargs):
        """
        Override the paintEvent to create the layout for the cell.
        """
        if not self.layout():
            paint_layout = layout.hbox(self._action_items,
                                       spacing=1,
                                       margins=(1, 1, 1, 1))
            self.setLayout(paint_layout)
            self.layout_set = True
        super(RowActionItem, self).paintEvent(*args, **kwargs)