# -*- 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 *
import platform
import pygame
from pygame.locals import *
from string import whitespace
from openexp._keyboard.keyboard import Keyboard
from openexp.backend import configurable

# Whitespace, backspace, and empty strings are not acceptable names for keys.
# These should be converted to descriptions, e.g. '\t' to 'tab'
invalid_unicode = [u'', u'\x08', u'\x7f'] + list(whitespace)
# On mac arrow keys are not accepted as valid input either. Add them to this list
if platform.system() == "Darwin":
    invalid_unicode += [
        u'\uf702',  # left
        u'\uf703',  # right
        u'\uf700',  # up
        u'\uf701',  # down
        u'\uf729',  # home
        u'\uf72b',  # end
        u'\uf72c',  # page up
        u'\uf72d',  # page down
        u'\uf728',  # delete
        u'\uf739',  # numlock
    ]


class Legacy(Keyboard):

    r"""This is a keyboard backend built on top of PyGame. For function
    specifications and docstrings, see `openexp._keyboard.keyboard`.
    """
    def __init__(self, experiment, **resp_args):

        pygame.init()
        self.key_code_to_name = {}
        self.key_name_to_code = {}
        for i in dir(pygame):
            if i[:2] == u"K_":
                code = getattr(pygame, i)
                name1 = self.key_name(code).lower()
                name2 = name1.upper()
                name3 = i[2:].lower()
                name4 = name3.upper()
                self.key_code_to_name[code] = [name1, name2, name3, name4]
                try:
                    i = int(name5)
                    self.key_code_to_name[code].append(name5)
                except:
                    pass
                self.key_name_to_code[name1] = code
                self.key_name_to_code[name2] = code
                self.key_name_to_code[name3] = code
                self.key_name_to_code[name4] = code
        self.persistent_virtual_keyboard = False
        Keyboard.__init__(self, experiment, **resp_args)

    @configurable
    def get_key(self):

        return self._get_key_event(pygame.KEYDOWN)

    @configurable
    def get_key_release(self):

        return self._get_key_event(pygame.KEYUP)

    def _get_key_event(self, event_type):

        start_time = pygame.time.get_ticks()
        time = start_time
        keylist = self.keylist
        timeout = self.timeout
        while True:
            time = pygame.time.get_ticks()
            # Some input methods send multiple key events at the same time,
            # for example when composing a multicharacter Chinese or Japanese
            # string. That's why we process up all events, rather than
            # assuming that there's only a single relevant event in the queue.
            key = u''
            for event in pygame.event.get(event_type):
                if event.key == pygame.K_ESCAPE:
                    self.experiment.pause()
                # KEYUP events don't have a unicode property, so in that case
                # we fall back to converting the key code straight to an ASCII
                # value. This is not great, because it assumes a QWERTY
                # keyboard layout.
                if hasattr(event, u'unicode'):
                    ucode = event.unicode
                elif event.key < 128:
                    ucode = chr(event.key)
                else:
                    ucode = u''
                if ucode in invalid_unicode:
                    key += self.key_name(event.key)
                else:
                    key += ucode
            if key and (keylist is None or key in keylist):
                return key, time
            if timeout is not None and time - start_time >= timeout:
                break
        return None, time

    def get_mods(self):

        l = []
        mods = pygame.key.get_mods()
        if mods & KMOD_LSHIFT or mods & KMOD_RSHIFT or mods & KMOD_SHIFT:
            l.append(u"shift")
        if mods & KMOD_LCTRL or mods & KMOD_RCTRL or mods & KMOD_CTRL:
            l.append(u"ctrl")
        if mods & KMOD_LALT or mods & KMOD_RALT or mods & KMOD_ALT:
            l.append(u"alt")
        if mods & KMOD_LMETA or mods & KMOD_RMETA or mods & KMOD_META:
            l.append(u"meta")
        return l

    def valid_keys(self):

        return sorted(self.key_name_to_code.keys())

    def synonyms(self, key):

        # If the key is not familiar, simply return it plus its string
        # representation.
        if key not in self.key_name_to_code:
            return [key, safe_decode(key)]
        return self.key_code_to_name[self.key_name_to_code[key]]

    def flush(self):

        keypressed = False
        for event in pygame.event.get():
            if event.type == KEYDOWN:
                keypressed = True
                if event.key == pygame.K_ESCAPE:
                    self.experiment.pause()
        pygame.event.pump()
        return keypressed

    def key_name(self, key):

        return str(pygame.key.name(key)).replace(u'[', u'').replace(u']',
                                                                    u'')

    def _keycode_to_str(self, keycode):

        return safedecode(pygame.key.name(keycode)) \
            .replace(u'[', u'').replace(u']', u'').lower()


# Non PEP-8 alias for backwards compatibility
legacy = Legacy
