# -*- coding: UTF-8 -*-
## @file
## @package pyKey_win
#  @brief Simulation von Tastendrücken
#
"""!
Für Windows zusammengefasste und leicht eingeschränkte Version des Scripts
[pyKey von Gautham Venkataraman](https://github.com/gauthsvenkat/pyKey)
"""

import ctypes

# ==============================================================================
# Windows-spezifisch:

VK_LBUTTON  = 0x01
VK_CONTROL  = 0x11
VK_R        = 0x52

win_keys= {
'ESC' : 0x01,
'!' : 0x02,
'1' : 0x02,
'@' : 0x03,
'2' : 0x03,
'#' : 0x04,
'3' : 0x04,
'$' : 0x05,
'4' : 0x05,
'%' : 0x06,
'5' : 0x06,
'^' : 0x07,
'6' : 0x07,
'&' : 0x08,
'7' : 0x08,
'*' : 0x09,
'8' : 0x09,
'(' : 0x0A,
'9' : 0x0A,
')' : 0x0B,
'0' : 0x0B,
'_' : 0x0C,
'-' : 0x0C,
'+' : 0x0D,
'=' : 0x0D,
'BKSP' : 0x0E,
'TAB' : 0x0F,
'Q' : 0x10,
'W' : 0x11,
'E' : 0x12,
'R' : 0x13,
'T' : 0x14,
'Y' : 0x15,
'U' : 0x16,
'I' : 0x17,
'O' : 0x18,
'P' : 0x19,
'{' : 0x1A,
'[' : 0x1A,
'}' : 0x1B,
']' : 0x1B,
'ENTER' : 0x1C,
'CTRL' : 0x1D,
'A' : 0x1E,
'S' : 0x1F,
'D' : 0x20,
'F' : 0x21,
'G' : 0x22,
'H' : 0x23,
'J' : 0x24,
'K' : 0x25,
'L' : 0x26,
':' : 0x27,
';' : 0x27,
'"' : 0x28,
"'" : 0x28,
'TILDE' : 0x29,
'BACKTICK' : 0x29,
'LSHIFT' : 0x2A,
'|' : 0x2B,
'BSLASH' : 0x2B,
'Z' : 0x2C,
'X' : 0x2D,
'C' : 0x2E,
'V' : 0x2F,
'B' : 0x30,
'N' : 0x31,
'M' : 0x32,
'<' : 0x33,
',' : 0x33,
'>' : 0x34,
'.' : 0x34,
'?' : 0x35,
'/' : 0x35,
'RSHIFT' : 0x36,
'PRTSC' : 0x37,
'*' : 0x37,
'ALT' : 0x38,
'SPACEBAR' : 0x39,
'CAPSLOCK' : 0x3A,
'F1' : 0x3B,
'F2' : 0x3C,
'F3' : 0x3D,
'F4' : 0x3E,
'F5' : 0x3F,
'F6' : 0x40,
'F7' : 0x41,
'F8' : 0x42,
'F9' : 0x43,
'F10' : 0x44,
'NUMLOCK' : 0x45,
'SCROLL_LOCK' : 0x46,
'HOME' : 0x47,
'NUM7' : 0x47,
'UP' : 0x48,
'NUM8' : 0x48,
'PGUP' : 0x49,
'NUM9' : 0x49,
'NUM-' : 0x4A,
'LEFT' : 0x4B,
'NUM4' : 0x4B,
'NUM5' : 0x4C,
'RIGHT' : 0x4D,
'NUM6' : 0x4D,
'NUM+' : 0x4E,
'END' : 0x4F,
'NUM1' : 0x4F,
'DOWN' : 0x50,
'NUM2' : 0x50,
'PGDN' : 0x51,
'NUM3' : 0x51,
'INS' : 0x52,
'NUM0' : 0x52,
'DEL' : 0x53,
'NUM.' : 0x53,
'F11' : 0x85,
'F12' : 0x86,
'q' : 0x10,
'w' : 0x11,
'e' : 0x12,
'r' : 0x13,
't' : 0x14,
'y' : 0x15,
'u' : 0x16,
'i' : 0x17,
'o' : 0x18,
'p' : 0x19,
'a' : 0x1E,
's' : 0x1F,
'd' : 0x20,
'f' : 0x21,
'g' : 0x22,
'h' : 0x23,
'j' : 0x24,
'k' : 0x25,
'l' : 0x26,
'z' : 0x2C,
'x' : 0x2D,
'c' : 0x2E,
'v' : 0x2F,
'b' : 0x30,
'n' : 0x31,
'm' : 0x32,
}

GetAsyncKeyState = ctypes.windll.user32.GetAsyncKeyState
SendInput        = ctypes.windll.user32.SendInput

# C struct redefinitions
PUL = ctypes.POINTER(ctypes.c_ulong)
class KeyBdInput(ctypes.Structure):
    _fields_ = [("wVk", ctypes.c_ushort),
                ("wScan", ctypes.c_ushort),
                ("dwFlags", ctypes.c_ulong),
                ("time", ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class HardwareInput(ctypes.Structure):
    _fields_ = [("uMsg", ctypes.c_ulong),
                ("wParamL", ctypes.c_short),
                ("wParamH", ctypes.c_ushort)]

class MouseInput(ctypes.Structure):
    _fields_ = [("dx", ctypes.c_long),
                ("dy", ctypes.c_long),
                ("mouseData", ctypes.c_ulong),
                ("dwFlags", ctypes.c_ulong),
                ("time",ctypes.c_ulong),
                ("dwExtraInfo", PUL)]

class Input_I(ctypes.Union):
    _fields_ = [("ki", KeyBdInput),
                 ("mi", MouseInput),
                 ("hi", HardwareInput)]

class Input(ctypes.Structure):
    _fields_ = [("type", ctypes.c_ulong),
                ("ii", Input_I)]


#Presses a key and holds it until explicitly called the releaseKey function.
def pressKey(key=None):
    assert key is not None, "No keys are given (key=None). Please check your code"
    assert key in win_keys, "The key({}) you're trying to press does not exist! Please check for any spelling errors.".format(key)

    extra = ctypes.c_ulong(0)
    ii_ = Input_I()

    #These keys use the extendedkey prefix 0xE0, directly simulating the actual key rather than the redundant ones in numpad
    if key in ['INS', 'HOME', 'PGUP', 'PGDN', 'END', 'DEL', 'UP', 'DOWN', 'LEFT', 'RIGHT']:
        ii_.ki = KeyBdInput( 0, win_keys[key], 0x0008 | 0x0001, 0, ctypes.pointer(extra) )
    #Else normal use
    else: ii_.ki = KeyBdInput( 0, win_keys[key], 0x0008, 0, ctypes.pointer(extra) )

    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

#Releases a key that was pressed using pressKey. NEVER FORGET TO USE THIS AFTER USING pressKey()
def releaseKey(key=None):
    assert key is not None, "No keys are given (key=None). Please check your code"
    assert key in win_keys, "The key({}) you're trying to release does not exists! Please check for any spelling errors.".format(key)

    extra = ctypes.c_ulong(0)
    ii_ = Input_I()

    if key in ['INS', 'HOME', 'PGUP', 'PGDN', 'END', 'DEL', 'UP', 'DOWN', 'LEFT', 'RIGHT']:
        ii_.ki = KeyBdInput( 0, win_keys[key], 0x0008 | 0x0002 | 0x0001, 0, ctypes.pointer(extra) )
    else: ii_.ki = KeyBdInput( 0, win_keys[key], 0x0008 | 0x0002, 0, ctypes.pointer(extra) )

    x = Input( ctypes.c_ulong(1), ii_ )
    ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))

#Presses a key and releases it. If sec argument is given, this function will press and hold a key for that many seconds.
def press(key=None):
    assert key is not None, "No keys are given (key=None). Please check your code"
    assert key in win_keys, "The key({}) you're trying to release does not exists! Please check for any spelling errors.".format(key)

    shift_flag = None #to check if shift is toggled

    if key in '!@#$%^&*()_+}{~|:"<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ':
        pressKey('LSHIFT')
        shift_flag = True

    pressKey(key)
    releaseKey(key)

    if shift_flag: #release shift if shift is toggled
        releaseKey('LSHIFT')
        shift_flag = None

'''Send a sequence of key presses. If sequence is a string, it'll be simulated. If sequence is a list of keys, it'll be simulated. If sequence is a dict, it will be interpreted as a key value pair, whose key is the key that is going to be pressed and the value as how long to hold the key for (in seconds).'''
def sendSequence(seq=None):
    assert seq is not None, "No sequence is given (seq=None). Please check your code"
    #if sequence is a string
    if isinstance(seq, str):
         for c in seq:
            press(c)

    #if sequence is a list
    if isinstance(seq, list):
        for key in seq:
            press(key)

    #if sequence is a dict
    if isinstance(seq, dict):
        for key, sec in seq.items():
            press(key, sec)

# ------------------------------------------------------------------------------

def waitCtrlR_Released():
    while GetAsyncKeyState(VK_CONTROL) <> 0:
        pass
    while GetAsyncKeyState(VK_R) <> 0:
        pass
