# -*- coding: utf-8 -*-

import json
import sys
import threading
import time
import win32api
import win32con
import winsound
import ctypes
from image_search import get_screen_area_as_image, load_image_from_file, search_image_in_image
from overlay_label import OverlayLabel
from keyboard_input import keyb_down, keyb_up


LMB = win32con.VK_LBUTTON
F4 = win32con.VK_F4
F10 = win32con.VK_F10
NUM_4 = win32con.VK_NUMPAD4
NUM_6 = win32con.VK_NUMPAD6
KEY_1 = 0x31
KEY_2 = 0x32
KEY_3 = 0x33
KEY_E = 0x45
KEY_R = 0x52

EMPTY_WEAPONS_LIST = [
    {
        "name": "None",
        "rpm": 6000,
        "check_image": None,
        "check_area": [1500, 950, 1735, 1030],
        "pattern": [
            [0,0],
        ]
    },
]

# for cursor detector
class POINT(ctypes.Structure):
    _fields_ = [('x', ctypes.c_int),
                ('y', ctypes.c_int)]

class CURSORINFO(ctypes.Structure):
    _fields_ = [('cbSize', ctypes.c_uint),
                ('flags', ctypes.c_uint),
                ('hCursor', ctypes.c_void_p),
                ('ptScreenPos', POINT)]

def beep_on():
    winsound.Beep(2000, 100)


def beep_off():
    winsound.Beep(1000, 100)


def beep_exit():
    winsound.Beep(500, 500)


def mouse_move_relative(dx, dy):
    win32api.mouse_event(win32con.MOUSEEVENTF_MOVE, int(dx), int(dy), 0, 0)


def lmb_down():
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0)


def lmb_up():
    win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0)


def rmb_down():
    win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTDOWN, 0, 0)


def rmb_up():
    win32api.mouse_event(win32con.MOUSEEVENTF_RIGHTUP, 0, 0)


def is_lmb_pressed():
    return win32api.GetKeyState(LMB) < 0


def cursor_detector():
    # Load and set argument types
    GetCursorInfo = ctypes.windll.user32.GetCursorInfo
    GetCursorInfo.argtypes = [ctypes.POINTER(CURSORINFO)]
    # Initialize the output structure
    info = CURSORINFO()
    info.cbSize = ctypes.sizeof(info)
    # Do it!
    if GetCursorInfo(ctypes.byref(info)):
        if info.flags & 0x00000001:
            return True
        else:
            return False
    else:
        print("WARNING: Cursor detector is not running!")
        

def load_weapons(weapon_filename):
    weapons_list = EMPTY_WEAPONS_LIST
    current_weapon_index = 0

    if not weapon_filename:
        print("WARNING: Filename with weapons data was not set! Meh!?")
        return weapons_list, current_weapon_index

    weapon_filepath = "./weapon_data/{}.json".format(weapon_filename)

    print("DEBUG: Trying to open and load data from {}".format(weapon_filepath))
    try:
        with open(weapon_filepath) as f:
            data = json.load(f)
            weapons_data = data["weapons"]
    except:
        print("ERROR: Can not open/read file with weapon data. No file? Corrupted? WTF!?")
        print("INFO: Since last error I will use default EMPTY_WEAPONS_LIST. Go check your data files, okay?")
        return weapons_list, current_weapon_index
    print("DEBUG: Not sure but looks like everything is okay :)")

    weapons_list = weapons_data

    for i, weapon in enumerate(weapons_list):
        if weapon["check_image"]:
            image = load_image_from_file("./weapon_data/{}_img\{}".format(weapon_filename, weapon["check_image"]))
            weapons_list[i]["image"] = image
        else:
            weapons_list[i]["image"] = None

    return weapons_list, current_weapon_index


def toggle_recoil(no_recoil):
    if no_recoil:
        beep_off()
    else:
        beep_on()
    return not no_recoil


def prev_weapon(weapons_list, current_weapon_index):
    if current_weapon_index < 1:
        current_weapon_index = len(weapons_list) - 1
    else:
        current_weapon_index -= 1
    return current_weapon_index


def next_weapon(weapons_list, current_weapon_index):
    if current_weapon_index > len(weapons_list) - 2:
        current_weapon_index = 0
    else:
        current_weapon_index += 1
    return current_weapon_index


def get_tick(rpm):
    rps = rpm/60
    mstick = 1000.0/rps
    stick = round(mstick/1000, 3)
    return stick


def construct_overlay(overlay, weapons_list, current_weapon_index, no_recoil):
    recoil_data = "ON" if no_recoil else "OFF"
    bg_data = "#acffac" if no_recoil else "#ffacac"
    recoil_string = "NoRecoil: {}".format(recoil_data)
    weapon_string = "Weapon: {}".format(weapons_list[current_weapon_index]["name"])
    length = max(len(recoil_string), len(weapon_string))
    overlay_string = "{}\n{}".format(recoil_string.ljust(length), weapon_string.ljust(length))
    overlay.set_bg(bg_data)
    overlay.set_text(overlay_string)


def process_no_recoil(overlay, weapons_list, current_weapon_index, no_recoil):
    shot_index = 0
    shot_tick = get_tick(weapons_list[current_weapon_index]["rpm"])
    while is_lmb_pressed():
        current_pattern = weapons_list[current_weapon_index]["pattern"]
        if weapons_list[current_weapon_index]["name"] == "Peacekeeper":
            time.sleep(110 / 1000)
            lmb_up()
            keyb_down(KEY_R)
            time.sleep(110 / 1000)
            keyb_up(KEY_R)
            time.sleep(110 / 2000)
            keyb_down(KEY_3)
            time.sleep(110 / 2000)
            keyb_up(KEY_3)
            time.sleep(110 / 2000)
            keyb_down(KEY_2)
            time.sleep(100 / 2000)
            keyb_up(KEY_2)
            # time.sleep(750 / 2000)

        elif shot_index < len(current_pattern) - 1:
            dx = -current_pattern[shot_index][0]
            dy = -current_pattern[shot_index][1]
            mouse_move_relative(dx, dy)
            time.sleep(shot_tick)
            shot_index += 1
            construct_overlay(overlay, weapons_list, current_weapon_index, no_recoil)


def detect_current_weapon(weapons_list):
    for index, weapon in enumerate(weapons_list):
        if weapon["image"] is not None:
            found_xy = None
            try:
                image_to_check = get_screen_area_as_image(weapon["check_area"])
                found_xy = search_image_in_image(weapon["image"], image_to_check)
            except:
                print("Can not read images. Resolution is wrong? Configs? Dunno :(")
            if found_xy:
                return index
    return None


class WeaponDetectorThread(threading.Thread):
    def __init__(self, weapon_list):
        threading.Thread.__init__(self)
        self.weapon_list = weapon_list
        self.out = None
        self.no_recoil = False
        self.shutdown = False

    def run(self):
        while not self.shutdown:
            if self.no_recoil:
                weapon_autodetect = detect_current_weapon(self.weapon_list)
                self.out = weapon_autodetect
            time.sleep(0.05)

    def terminate(self):
        self.shutdown = True


def main(weapon_filename):
    running = True
    no_recoil = False
    weapons_list, current_weapon_index = load_weapons(weapon_filename)
    overlay = OverlayLabel()
    overlay.set_size(20, 2)  # size in symbols
    print("INFO: Starting WeaponDetector daemon...")
    weapon_detector = WeaponDetectorThread(weapons_list)
    weapon_detector.setDaemon(True)
    weapon_detector.start()
    print("INFO: Everything looks ok, so I'm going to my general routine ;)")

    while running:
        if weapon_detector.out is not None:
            current_weapon_index = weapon_detector.out
        construct_overlay(overlay, weapons_list, current_weapon_index, no_recoil)
        if win32api.GetAsyncKeyState(F4):
            no_recoil = toggle_recoil(no_recoil)
            weapon_detector.no_recoil = no_recoil
            time.sleep(0.2)
        if win32api.GetAsyncKeyState(F10):
            running = not running
            beep_exit()
            weapon_detector.terminate()
            print("INFO: Exiting!")
            time.sleep(0.5)
        if win32api.GetAsyncKeyState(NUM_4):
            current_weapon_index = prev_weapon(weapons_list, current_weapon_index)
            time.sleep(0.2)
        if win32api.GetAsyncKeyState(NUM_6):
            current_weapon_index = next_weapon(weapons_list, current_weapon_index)
            time.sleep(0.2)
        if is_lmb_pressed() and no_recoil and not cursor_detector():
            process_no_recoil(overlay, weapons_list, current_weapon_index, no_recoil)
        time.sleep(0.01)


if __name__ == "__main__":
    if len(sys.argv) < 2 or (len(sys.argv) == 2 and sys.argv[1] == "help"):
        print("Usage: python " + sys.argv[0] + " <weapons_data_filename>")
        print("Example: python " + sys.argv[0] + " apex")
    else:
        data_filename = str(sys.argv[1])
        main(data_filename)