#!/usr/bin/env python3
# -*- coding: cp949 -*-
#
#Copyright 2018 Sodium "natoriusushio" Chloride
#
#Released under the MIT license
#
#Permission is hereby granted, free of charge, to any person obtaining a copy
#of this software and associated documentation files (the "Software"),
#to deal in the Software without restriction, including without limitation
#the rights to use, copy, modify, merge, publish, distribute, sublicense,
#and/or sell copies of the Software, and to permit persons to whom
#the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
#DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
#TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
#OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#https://opensource.org/licenses/mit-license.php

import ctypes
import datetime
import infi.systray
import hangul_jamo
import json
import keyboard
import threading
import time
import tkinter
import win32api
import win32gui
import win32process

# ==== definition area ====
# == variable ==
abort = False
autophagy = False
backspaceisreleased = False
destroy = False
end = False
firstloop = True

chkfghWnd = 0

boxname = ""
converted = ""
chkfgWndt = ""
source = ""

systray = None

# == character map ==
#alphabetmap = ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p",
#            "a", "s", "d", "f", "g", "h", "j", "k", "l",
#            "z", "x", "c", "v", "b", "n", "m",
#            "Q", "W", "E", "R", "T", "O", "P"]
#jamomap = ["げ", "じ", "ぇ", "ぁ", "さ", "に", "づ", "ち", "だ", "つ",
#        "け", "い", "し", "ぉ", "ぞ", "で", "っ", "た",  "び",
#        "せ", "ぜ", "ず", "そ", "ば", "ぬ", "ぱ",
#        "こ", "す", "え", "あ", "ざ", "ぢ", "て"]

# == win32api ==
# pywin32
# https://github.com/mhammond/pywin32
# http://timgolden.me.uk/pywin32-docs/index.html
def attach_thread_input(current, target, boolean):
    win32process.AttachThreadInput(current, target, boolean)

def client_to_screen(target, client_x, client_y):
    coordinates = (client_x, client_y)
    screen_x, screen_y = win32gui.ClientToScreen(target, coordinates)
    return screen_x, screen_y

def find_window(classname, windowname):
    result = win32gui.FindWindow(classname, windowname)
    return result

def get_caret_pos():
    result = win32gui.GetCaretPos()
    return result

def get_current_thread_id():
    result = win32api.GetCurrentThreadId()
    return result

def get_foreground_window():
    result = win32gui.GetForegroundWindow()
    return result

def get_system_metrics():
    screen_x = win32api.GetSystemMetrics(0)
    screen_y = win32api.GetSystemMetrics(1)
    return screen_x, screen_y

def get_window_rect(hWnd):
    topleft_x, topleft_y, bottomright_x, bottomright_y \
    = win32gui.GetWindowRect(hWnd)
    return topleft_x, topleft_y, bottomright_x, bottomright_y

def get_window_text(hWnd):
    result = win32gui.GetWindowText(hWnd)
    return result

def get_window_thread_process_id(hWnd):
    targetthreadid, targetprocessid = win32process.GetWindowThreadProcessId(
                                                                        hWnd)
    return targetthreadid, targetprocessid

def set_foreground_window(hWnd):
    win32gui.SetForegroundWindow(hWnd)

# == function ==
def apoptosis(boolean):
    global destroy
    destroy = boolean
    global end
    end = boolean
    global autophagy
    autophagy = boolean

# keyboard
# https://github.com/boppreh/keyboard
def terminator():
    keyboard.add_hotkey("ctrl+alt+del", apoptosis, args=[True])

def backspace_is_released(boolean):
    global backspaceisreleased
    backspaceisreleased = boolean

def add_hotkey_backspace_is_released():
    keyboard.add_hotkey("backspace", backspace_is_released,
                        args=[True], trigger_on_release=True)

def get_convwindow_size(): # convwindow = window for converting
    topleft_x =0
    topleft_y =0
    bottomright_x =144
    bottomright_y =21
    return bottomright_x, bottomright_y

# I referred to these topics for writing the function below.
# https://stackoverflow.com/questions/19724360/python-get-caret-position
# http://timgolden.me.uk/python/win32_how_do_i/find-the-screen-resolution.html
def get_coordinates():
        targetwindow = get_foreground_window()
        targetthreadid, targetprocessid = get_window_thread_process_id(
                                                                targetwindow)
        currentthreadid = get_current_thread_id()
        try:
            attach_thread_input(currentthreadid, targetthreadid, True)
            clientcaret_x, clientcaret_y = get_caret_pos()
        finally:
            attach_thread_input(currentthreadid, targetthreadid, False)
        if (clientcaret_x, clientcaret_y) == (None, None):
            screen_x, screen_y = get_system_metrics()
            convwin_x, convwin_y = get_convwindow_size()
            screencaret_x = (screen_x - convwin_x) // 2
            screencaret_y = 2 * (screen_y - convwin_y) // 3
        else:
            if (clientcaret_x, clientcaret_y) == (0, 0):
                # topleft=tpl, bottomright=btmr
                tpl_x, tpl_y, btmr_x, btmr_y = get_window_rect(targetwindow)
                convwin_x, convwin_y = get_convwindow_size()
                tmp_x = (btmr_x - tpl_x - convwin_x) // 2
                tmp_y = 2 * (btmr_y - tpl_y - convwin_x) // 3
                screencaret_x = tpl_x + tmp_x
                screencaret_y = tpl_y + tmp_y
            else:
                screencaret_x, screencaret_y = client_to_screen(
                                                        targetwindow,
                                                        clientcaret_x,
                                                        clientcaret_y)
        return screencaret_x, screencaret_y

def get_source(screencaret_x, screencaret_y):
    global destroy
    destroy = False
    label = "conv"
    identifier = datetime.datetime.now().strftime("%f")
    global boxname
    boxname = label+identifier
    root = tkinter.Tk()
    root.title(boxname)
    root.attributes("-topmost", True)
    root.overrideredirect(1)
    coordinate = "+%s+%s" % (screencaret_x, screencaret_y)
    root.geometry(coordinate)
#    getentry = tkinter.Entry(root, font=("Batang", 12))
    getentry = tkinter.Entry()
    getentry.config(background="azure")
    def get_entry(event):
        keyboard.release("enter")
        global source
        source = getentry.get()
        global destroy
        destroy = True
    def terminate(event):
        keyboard.release("\\")
        keyboard.release("ctrl")
        global end
        end = True
        fgw = get_foreground_window()
        fgwt = get_window_text(fgw)
        global destroy
        destroy = True
    getentry.pack()
    getentry.focus_set()
    root.bind("<Return>", get_entry)
    root.bind("<Control-\\>", terminate)
    while destroy == False:
        time.sleep(0.001) # for reducing CPU usage
        root.update()
        fgw = get_foreground_window()
        fgwt = get_window_text(fgw)
        if fgwt == boxname:
            pass
        else:
            target = find_window("TkTopLevel", boxname)
            set_foreground_window(target)
    destroy = False
    getentry.destroy()
    root.destroy()

#def convert_alphabet_to_jamo():
#    for alp, jam in zip(alphabetmap, jamomap):
#        keyboard.add_abbreviation(alp, jam)

# str.maketrans()
# https://www.tutorialspoint.com/python3/string_maketrans.htm
def convert_alphabet_to_jamo(alphabet):
    translatetable = str.maketrans("qwertyuiopasdfghjklzxcvbnmQWERTOP",
                                   "げじぇぁさにづちだつけいしぉぞでったびせぜずそばぬぱこすえあざぢて")
    result = alphabet.translate(translatetable)
    return result

def show_source(screencaret_x, screencaret_y):
    global source
    if len(source) > 0:
        showsource = convert_alphabet_to_jamo(source)
        global destroy
        destroy = False
        label = "conv"
        identifier = datetime.datetime.now().strftime("%f")
        global boxname
        boxname = label+identifier
        root = tkinter.Tk()
        root.title(boxname)
        root.attributes("-topmost", True)
        root.overrideredirect(1)
        coordinate = "+%s+%s" % (screencaret_x, screencaret_y)
        root.geometry(coordinate)
#        showentry = tkinter.Entry(root, font=("Batang", 12))
        showentry = tkinter.Entry()
        showentry.insert(tkinter.END, showsource)
        showentry.config(background="PaleTurquoise1")
        def end_entry(event):
            keyboard.release("enter")
            global destroy
            destroy = True
        def terminate(event):
            keyboard.release("\\")
            keyboard.release("ctrl")
            global end
            end = True
            fgw = get_foreground_window()
            fgwt = get_window_text(fgw)
            global destroy
            destroy = True
        showentry.pack()
        showentry.focus_set()
        root.bind("<Return>", end_entry)
        root.bind("<Control-\\>", terminate)
        while destroy == False:
            time.sleep(0.001) # for reducing CPU usage
            root.update()
            fgw = get_foreground_window()
            fgwt = get_window_text(fgw)
            if fgwt == boxname:
                pass
            else:
                target = find_window("TkTopLevel", boxname)
                set_foreground_window(target)
        destroy = False
        showentry.destroy()
        root.destroy()
    else:
        pass

# hangul-jamo
# https://github.com/jonghwanhyeon/hangul-jamo
def convert_jamo_to_hangul(jamosource):
    result = hangul_jamo.compose(jamosource)
    return result

def threading_start(threaddef, threadname, targetname):
    threadname = threading.Thread(target=threaddef, name=targetname)
    threadname.start()

def write_word(word):
    time.sleep(0.001)
    keyboard.write(word)

def release_keys():
    keyboard.release("backspace")
    keyboard.release("space")
    keyboard.release("shift")
    keyboard.release("ctrl")
    keyboard.release("enter")

def unhook_all():
    keyboard.unhook_all()

def send_backspace():
    keyboard.send("backspace")

def send_enter():
    keyboard.send("enter")

def sleep_1ms():
    time.sleep(0.001)

def sleep_5cs():
    time.sleep(0.05)

# infi.systray
# https://github.com/Infinidat/infi.systray
def define_systray():
    global systray
    systray = infi.systray.SysTrayIcon("icon-ko.ico", "now converting")

def start_systray():
    systray.start()

def shutdown_systray():
    systray.shutdown()

# ==== algorithm area ====
# something like pseudocode oriented programming style ;-)

print("hi")
define_systray()
start_systray()

#print("Select window, then press shift.") # for testing
#keyboard.wait("shift") # for testing

chkfghWnd = get_foreground_window() # check foreground handle of window
chkfgWndt = get_window_text(chkfghWnd) # check foreground window text
if "conv" in chkfgWndt:
    print("Another process is already running!")
    print("Program will be aborted.")
    end = True
    abort = True

while end == False:
    fghWnd = 0 # foreground handle of window
    screencaret_x = 0
    screencaret_y = 0

    backspaceisreleased = False
    split = False
    thread1 = None
    thread2 = None

    source = ""
    converted = ""

    optionlist = []

    release_keys()
    unhook_all()
    add_hotkey_backspace_is_released()

# for avoiding a confliction between this program
# and the ctrl+alt+del security option window
    terminator()

    if firstloop == False:
        sleep_1ms()

    fghWnd = get_foreground_window()

    screencaret_x, screencaret_y = get_coordinates()
    thread1 = get_source(screencaret_x, screencaret_y)
    threading_start(thread1, "sourcethread", "sourcethread")
    if end == True:
        break

    thread2 = show_source(screencaret_x, screencaret_y)
    threading_start(thread2, "showthread", "showthread")
    if end == True:
        break

    if len(source) > 0:
        jamosource = convert_alphabet_to_jamo(source)
        converted = convert_jamo_to_hangul(jamosource)
    else:
        converted = ""

    set_foreground_window(fghWnd)
    
    if len(converted) > 0:
        write_word(converted)
    else:
        if backspaceisreleased == True:
            send_backspace()
        else:
            send_enter()
    firstloop = False

if abort == False:
    set_foreground_window(fghWnd)
release_keys()
unhook_all()
sleep_5cs()
shutdown_systray()
sleep_5cs()
if autophagy == False:
    print("bye")
else:
    print("adieu")
