import logging
import math
import time
import typing

import cv2
import numpy as np

if typing.TYPE_CHECKING:
    from fishy.engine.fullautofisher.engine import FullAuto

from fishy.engine.fullautofisher.mode.imode import IMode
from pynput import keyboard, mouse

from fishy.helper.config import config

mse = mouse.Controller()
kb = keyboard.Controller()

offset = 0


def _update_factor(key, value):
    full_auto_factors = config.get("full_auto_factors", {})
    full_auto_factors[key] = value
    config.set("full_auto_factors", full_auto_factors)


def _get_factor(key):
    return config.get("full_auto_factors", {}).get(key)


class Calibrator(IMode):
    def __init__(self, engine: 'FullAuto'):
        self._callibrate_state = -1
        self.engine = engine

    @property
    def move_factor(self):
        return _get_factor("move_factor")

    @property
    def rot_factor(self):
        return _get_factor("rot_factor")

    # endregion

    def all_calibrated(self):
        return self.move_factor is not None and \
               self.rot_factor is not None and \
               self.move_factor != 0 and \
               self.rot_factor != 0

    def toggle_show(self):
        self.engine.show_crop = not self.engine.show_crop

    def _walk_calibrate(self):
        walking_time = 3

        coords = self.engine.get_coords()
        if coords is None:
            return

        x1, y1, rot1 = coords

        forward_key = config.get("forward_key", 'w')
        kb.press(forward_key)
        time.sleep(walking_time)
        kb.release(forward_key)

        time.sleep(0.5)

        coords = self.engine.get_coords()
        if coords is None:
            return
        x2, y2, rot2 = coords

        move_factor = math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) / walking_time
        _update_factor("move_factor", move_factor)
        logging.info(f"walk calibrate done, move_factor: {move_factor}")

    def _rotate_calibrate(self):
        from fishy.engine.fullautofisher.engine import FullAuto

        rotate_times = 50

        coods = self.engine.get_coords()
        if coods is None:
            return
        _, _, rot2 = coods

        for _ in range(rotate_times):
            mse.move(FullAuto.rotate_by, 0)
            time.sleep(0.05)

        coods = self.engine.get_coords()
        if coods is None:
            return
        x3, y3, rot3 = coods

        if rot3 > rot2:
            rot3 -= 360

        rot_factor = (rot3 - rot2) / rotate_times
        _update_factor("rot_factor", rot_factor)
        logging.info(f"rotate calibrate done, rot_factor: {rot_factor}")

    def run(self):
        self._walk_calibrate()
        self._rotate_calibrate()
        config.set("calibrate", False)
        logging.info("calibration done")
