from kmk.consts import UnicodeMode
from kmk.handlers.stock import passthrough
from kmk.keys import KC, make_key
from kmk.types import AttrDict, KeySequenceMeta
from kmk.util import get_wide_ordinal


def sequence_press_handler(key, state, KC, *args, **kwargs):
    old_keys_pressed = state.keys_pressed
    state.keys_pressed = set()

    for ikey in key.meta.seq:
        if not getattr(ikey, 'no_press', None):
            state.process_key(ikey, True)
            state.config._send_hid()
        if not getattr(ikey, 'no_release', None):
            state.process_key(ikey, False)
            state.config._send_hid()

    state.keys_pressed = old_keys_pressed

    return state


def simple_key_sequence(seq):
    return make_key(
        meta=KeySequenceMeta(seq),
        on_press=sequence_press_handler,
        on_release=passthrough,
    )


def send_string(message):
    seq = []

    for char in message:
        kc = KC[char]

        if char.isupper():
            kc = KC.LSHIFT(kc)

        seq.append(kc)

    return simple_key_sequence(seq)


IBUS_KEY_COMBO = simple_key_sequence((KC.LCTRL(KC.LSHIFT(KC.U)),))
RALT_KEY = simple_key_sequence((KC.RALT,))
U_KEY = simple_key_sequence((KC.U,))
ENTER_KEY = simple_key_sequence((KC.ENTER,))
RALT_DOWN_NO_RELEASE = simple_key_sequence((KC.RALT(no_release=True),))
RALT_UP_NO_PRESS = simple_key_sequence((KC.RALT(no_press=True),))


def compile_unicode_string_sequences(string_table):
    for k, v in string_table.items():
        string_table[k] = unicode_string_sequence(v)

    return AttrDict(string_table)


def unicode_string_sequence(unistring):
    '''
    Allows sending things like (╯°□°)╯︵ ┻━┻ directly, without
    manual conversion to Unicode codepoints.
    '''
    return unicode_codepoint_sequence([
        hex(get_wide_ordinal(s))[2:]
        for s in unistring
    ])


def generate_codepoint_keysym_seq(codepoint, expected_length=4):
    # To make MacOS and Windows happy, always try to send
    # sequences that are of length 4 at a minimum
    # On Linux systems, we can happily send longer strings.
    # They will almost certainly break on MacOS and Windows,
    # but this is a documentation problem more than anything.
    # Not sure how to send emojis on Mac/Windows like that,
    # though, since (for example) the Canadian flag is assembled
    # from two five-character codepoints, 1f1e8 and 1f1e6
    #
    # As a bonus, this function can be pretty useful for
    # leader dictionary keys as strings.
    seq = [KC.N0 for _ in range(max(len(codepoint), expected_length))]

    for idx, codepoint_fragment in enumerate(reversed(codepoint)):
        seq[-(idx + 1)] = KC.get(codepoint_fragment)

    return seq


def generate_leader_dictionary_seq(string):
    return tuple(generate_codepoint_keysym_seq(string, 1))


def unicode_codepoint_sequence(codepoints):
    kc_seqs = (
        generate_codepoint_keysym_seq(codepoint)
        for codepoint in codepoints
    )

    kc_macros = [
        simple_key_sequence(kc_seq)
        for kc_seq in kc_seqs
    ]

    def _unicode_sequence(key, state, *args, **kwargs):
        if state.config.unicode_mode == UnicodeMode.IBUS:
            state.process_key(
                simple_key_sequence(_ibus_unicode_sequence(kc_macros, state)),
                True,
            )
        elif state.config.unicode_mode == UnicodeMode.RALT:
            state.process_key(
                simple_key_sequence(_ralt_unicode_sequence(kc_macros, state)),
                True,
            )
        elif state.config.unicode_mode == UnicodeMode.WINC:
            state.process_key(
                simple_key_sequence(_winc_unicode_sequence(kc_macros, state)),
                True,
            )

    return make_key(on_press=_unicode_sequence)


def _ralt_unicode_sequence(kc_macros, state):
    for kc_macro in kc_macros:
        yield RALT_DOWN_NO_RELEASE
        yield kc_macro
        yield RALT_UP_NO_PRESS


def _ibus_unicode_sequence(kc_macros, state):
    for kc_macro in kc_macros:
        yield IBUS_KEY_COMBO
        yield kc_macro
        yield ENTER_KEY


def _winc_unicode_sequence(kc_macros, state):
    '''
    Send unicode sequence using WinCompose:

    http://wincompose.info/
    https://github.com/SamHocevar/wincompose
    '''
    for kc_macro in kc_macros:
        yield RALT_KEY
        yield U_KEY
        yield kc_macro