import functools
import struct
from time import sleep
from typing import TYPE_CHECKING

from mower.utils import config

from . import const

if TYPE_CHECKING:
    from .core import Client


def inject(control_type: int):
    """
    Inject control code, with this inject, we will be able to do unit test
    Args:
        control_type: event to send, TYPE_*
    """

    def wrapper(f):
        @functools.wraps(f)
        def inner(*args, **kwargs):
            package = struct.pack("!B", control_type) + f(*args, **kwargs)
            if args[0].parent.control_socket is not None:
                with args[0].parent.control_socket_lock:
                    args[0].parent.control_socket.send(package)
            return package

        return inner

    return wrapper


class ControlSender:
    def __init__(self, parent: "Client"):
        self.parent = parent

    @inject(const.TYPE_INJECT_KEYCODE)
    def keycode(
        self, keycode: int, action: int = const.ACTION_DOWN, repeat: int = 0
    ) -> bytes:
        """
        Send keycode to device
        Args:
            keycode: const.KEYCODE_*
            action: ACTION_DOWN | ACTION_UP
            repeat: repeat count
        """
        return struct.pack("!BIII", action, keycode, repeat, 0)

    @inject(const.TYPE_INJECT_TOUCH_EVENT)
    def touch(
        self, x: int, y: int, action: int = const.ACTION_DOWN, touch_id: int = -1
    ) -> bytes:
        """
        Touch screen
        Args:
            x: horizontal position
            y: vertical position
            action: ACTION_DOWN | ACTION_UP | ACTION_MOVE
            touch_id: Default using virtual id -1, you can specify it to emulate multi finger touch
        """
        x, y = max(x, 0), max(y, 0)
        if action == const.ACTION_DOWN:
            actions_button = 1
            buttons = 1
        elif action == const.ACTION_UP:
            actions_button = 1
            buttons = 0
        else:
            actions_button = 0
            buttons = 1

        return struct.pack(
            "!BqIIHHHII",
            action,
            touch_id,
            int(x),
            int(y),
            int(self.parent.resolution[0]),
            int(self.parent.resolution[1]),
            0xFFFF,
            actions_button,
            buttons,
        )

    @inject(const.TYPE_START_APP)
    def start_app(self):
        app_name = config.conf.APPNAME.encode("utf-8")
        length = len(app_name)
        return struct.pack(f"!B{length}s", length, app_name)

    def tap(self, x, y, hold_time: float = 0.07) -> None:
        """
        Tap on screen
        Args:
            x: horizontal position
            y: vertical position
            hold_time: hold time
        """
        self.touch(x, y, const.ACTION_DOWN)
        sleep(hold_time)
        self.touch(x, y, const.ACTION_UP)

    def send_keyevent(self, key_code, hold_time: float = 0.07):
        self.keycode(key_code, const.ACTION_DOWN)
        sleep(hold_time)
        self.keycode(key_code, const.ACTION_UP)
