"""
GUI elements that help with atomic symbols
Copyright Schrodinger, LLC. All rights reserved.
"""
import string
from schrodinger.application.matsci import atomicsymbols
from schrodinger.Qt import QtGui
from schrodinger.ui.qt import swidgets
[docs]class ElementValidator(swidgets.EnforcingValidatorMixin, QtGui.QValidator):
    """
    A QValidator to check if the input is a partial or valid atomic symbol.
    Partial symbols are 1- or 2-(Uu) letter strings that could begin a valid
    atomic symbol.
    """
[docs]    def validate(self, text, pos):
        """
        Check if the text is a partial or valid atomic symbol. Partial symbols
        are strings that could begin a valid atomic symbol.
        See PyQt documentation for argument and return value documentation.
        """
        check = str(text)
        if check in atomicsymbols.ATOMIC_SYMBOLS:
            self.last_valid_value = check
            return (self.Acceptable, text, pos)
        elif check in atomicsymbols.SYMBOL_START or not check:
            return (self.Intermediate, text, pos)
        else:
            return (self.Invalid, text, pos)  
[docs]class AtomNameLabelValidator(swidgets.EnforcingValidatorMixin,
                             QtGui.QValidator):
    """
    Manage validation of a atom name label.
    """
    VALID_LABEL_PUNCTUATION = '_()[]'
[docs]    def __init__(self, *args, **kwargs):
        """
        Overwrite parent to set the valid label characters dictionary on
        creation
        """
        super().__init__(*args, **kwargs)
        self.valid_label_chars = {
            x for x in string.ascii_letters + string.digits +
            self.VALID_LABEL_PUNCTUATION
        } 
[docs]    def validate(self, value, position):
        """
        See PyQt documentation for arguments and return values.
        """
        if not value:
            return self.Intermediate, value, position
        if all(x in self.valid_label_chars for x in value):
            return self.Acceptable, value, position
        else:
            return self.Invalid, value, position  
[docs]class ElementListValidator(swidgets.EnforcingValidatorMixin, QtGui.QValidator):
    """
    A QValidator to check if the inputs from a lineedit is a valid atomic symbols
    separated by comma. Empty lines are treated as intermediate state. All symbols
    are invalid. If input is not in atomicsymbol.ATOMIC_SYMBOLS then consider it
    intermediate state. All other states are consider valid state.
    """
[docs]    def validate(self, text, pos):
        """
        Check if the text is a valid atomic symbols separated by comma.
        See PyQt documentation for argument and return value documentation.
        """
        check = str(text)
        for token in check.split(','):
            if token.strip() == '':
                # An empty edit is temporarily acceptable
                return (self.Intermediate, text, pos)
            if not token.strip().isalpha():
                # Non-letters data is not OK
                return (self.Invalid, text, pos)
            if token.strip() not in atomicsymbols.ATOMIC_SYMBOLS:
                # Only letters, intermediate state
                return (self.Intermediate, text, pos)
        self.last_valid_value = check
        return (self.Acceptable, text, pos)  
[docs]class ElementEdit(swidgets.SLineEdit):
    """
    A QLineEdit that only accepts atomic symbols and always contains the last
    valid value when it loses keyboard focus
    """
[docs]    def __init__(self, default, layout=None, **kwargs):
        """
        Create an ElementEdit instance
        :type default: str
        :param default: Default atomic symbol
        :type layout: QBoxLayout
        :param layout: The layout to place this line edit into
        """
        dator = ElementValidator()
        swidgets.SLineEdit.__init__(self,
                                    default,
                                    validator=dator,
                                    width=30,
                                    always_valid=True,
                                    layout=layout,
                                    **kwargs)  
[docs]class ElementListEdit(swidgets.SLabeledEdit):
    """
    A QLineEdit that only accepts atomic symbols and always contains the last
    valid value when it loses keyboard focus
    """
[docs]    def __init__(self, text, layout=None, **kwargs):
        """
        Create an ElementEdit instance
        :type default: str
        :param default: Default atomic symbol
        :type layout: QBoxLayout
        :param layout: The layout to place this line edit into
        """
        dator = ElementListValidator()
        super(ElementListEdit, self).__init__(text=text,
                                              validator=dator,
                                              layout=layout,
                                              **kwargs)  
[docs]class CoarseGrainNameEdit(swidgets.SLineEdit):
    """
    A QLineEdit that only accepts valid coarse grain names and always contains
    the last valid value when it loses keyboard focus
    """
[docs]    def __init__(self, default, layout=None, **kwargs):
        """
        Create an CoarseGrainNameEdit instance
        :type default: str
        :param default: Default atomic symbol
        :type layout: QBoxLayout
        :param layout: The layout to place this line edit into
        """
        dator = AtomNameLabelValidator()
        swidgets.SLineEdit.__init__(self,
                                    default,
                                    validator=dator,
                                    width=50,
                                    always_valid=True,
                                    layout=layout,
                                    **kwargs)