# coding=utf-8

"""
This file is part of OpenSesame.

OpenSesame is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

OpenSesame is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenSesame.  If not, see <http://www.gnu.org/licenses/>.
"""
from libopensesame.py3compat import *
from libopensesame.exceptions import InvalidKeyName


CHAR_TO_NAME = {
    u'`': u'grave',
    u'~': u'asciitilde',
    u'^': u'asciicircum',
    u'%': u'percent',
    u'!': u'exclamation',
    u'"': u'doublequote',
    u'#': u'hash',
    u'$': u'dollar',
    u'&': u'ampersand',
    u'\'': u'apostrophe',
    u'*': u'asterisk',
    u'+': u'plus',
    u',': u'comma',
    u'-': u'minus',
    u'.': u'period',
    u'/': u'slash',
    u':': u'colon',
    u';': u'semicolon',
    u'=': u'equal',
    u'?': u'question',
    u'@': u'at',
    u'\\': u'backslash',
    u'|': u'bar',
    u'<': u'less',
    u'>': u'greater',
    u'[': u'bracketleft',
    u']': u'bracketright',
    u'(': u'parenleft',
    u')': u'parenright',
    u'{': u'braceleft',
    u'}': u'braceright',
    u'_': u'underscore'
}
NAME_TO_CHAR = {name: char for char, name in CHAR_TO_NAME.items()}


class KeyBabel:

    r"""Converts between different ways to represent keys through numeric
    values (keycodes), different names (strings), and None (for timeout).
    """
    def __init__(self, keyboard):
        r"""Constructor.

        Parameters
        ----------
        keyboard
            A Keyboard object
        """
        self._keyboard = keyboard

    def standard_name(self, key, shift=False):
        r"""Gives the standard name for a key. This is the shortest name, so it
        corresponds to a character if a character is available.

        Parameters
        ----------
        key
            The key to standardize.
        shift, optional
            Indicates whether the name should be cast to uppercase (True) or
            lowercase (False)

        Returns
        -------
        The standard name for the key.
        """
        short_name = (
            key
            if isinstance(key, str) and len(key) == 1
            else sorted(self.synonyms(key), key=len)[0]
        )
        return short_name.upper() if shift else short_name.lower()

    def synonyms(self, key):
        r"""Gives a set of synonyms for a key.

        Parameters
        ----------
        key
            The key to find synonyms for.

        Returns
        -------
        A set of synonyms.
        """
        if key is None:
            return self._none_synonyms()
        if isinstance(key, int):
            return self._keycode_synonyms(key)
        if isinstance(key, str):
            return self._str_synonyms(key)
        raise InvalidKeyName(key)

    def _none_synonyms(self):

        return {None, 'None', 'none', 'NONE'}

    def _keycode_synonyms(self, key):

        keyname = self._keyboard._keycode_to_str(key)
        return {key, keyname.upper(), keyname.lower()}

    def _str_synonyms(self, key):

        key_lower = key.lower()
        synonyms = {key, key.upper(), key_lower}
        if key_lower in CHAR_TO_NAME:
            # Add 'exclamation' for '!'
            synonyms.add(CHAR_TO_NAME[key_lower])
        elif key_lower in NAME_TO_CHAR:
            # Add '!' for 'exclamation'
            synonyms.add(NAME_TO_CHAR[key_lower])
        # Add timeout as NoneType
        if key_lower == u'none':
            synonyms.add(None)
        return synonyms
