# 定义抽象类
import json
import logging
from abc import ABC, abstractmethod

import uiautomator2 as u2


class AbsUi(ABC):

    def __init__(self, serial: str):
        """
        :param serial: 设备序列号。 例如 127.0.0.1:6555
            可以通过 `adb devices`  获取
        """
        # u2.logger.setLevel(logging.DEBUG)

        # 屏幕高度
        self.height = 0
        # 屏幕宽度
        self.width = 0
        self.d = u2.connect(serial)

        self._points = {}

        self.point_path = 'point.json'
        self.enable_click_monitor()
        self._func = []

    def enable_click_monitor(self):
        """
        启用点击监控，在屏幕上显示点击位置
        """
        # self.d.settings['operation_delay'] = (0.5, 0.5)  # 增加操作延迟以便观察
        self.d.debug = True
        self.d.toast.show('点击监控已启用')  # 显示提示

        # 确保有悬浮窗权限
        self.d.set_fastinput_ime(True)  # 启用ime
        self.d.show_float_window(True)  # 显示悬浮窗

        # 可选：打开开发者选项中的"指针位置"
        self.d.shell('settings put system pointer_location 1')

    def to_top_swipe(self):
        """
        滑动到屏幕最顶部，通过多次滑动确保到达顶部
        """
        width, height = self.get_screen_size()
        # 循环滑动直到无法继续滑动
        for _ in range(3):
            self.d.swipe(width // 2, height * 0.8, width // 2, height)  # 向上滑动
            # 短暂等待确保滑动完成
            self.d.sleep(0.1)

    def click_point(self, x: int, y: int):
        """
        点击指定坐标
        :param x: x坐标
        :param y: y坐标
        """
        self.d.click(x, y)

    def click_xpath(self, xpath: str):
        """
        点击指定xpath
        """
        el = self.d.xpath(xpath).get()
        if el:
            el.click()
        else:
            logging.warning(f"未找到元素: {xpath}")

    def to_next_swipe(self):
        """
        向上滑动一屏
        """
        width, height = self.get_screen_size()
        # 从屏幕下方向上滑动到顶部
        self.d.swipe(width // 2, height * 0.8, width // 2, height * 0.2, duration=0.1)  # 向上滑动

    def get_screen_size(self):
        """
        获取屏幕尺寸，宽度和高度
        """
        self.width, self.height = self.d.window_size()
        logging.info(f"屏幕尺寸: {self.width}x{self.height}")
        return self.width, self.height

    def drag_slider_ext(self, start_x: int, start_y: int, end_x: int, end_y: int, steps: int = 50):
        """
        精确拖动滑块
        :param start_x: 起始x坐标
        :param start_y: 起始y坐标
        :param end_x: 结束x坐标
        :param end_y: 结束y坐标
        :param steps: 步数，值越大滑动越平滑
        """
        self.d.swipe_ext(start_x, start_y, end_x, end_y, steps)

    def add_point(self, point=None):
        """
        添加坐标点
        point = {
                "name": "",
                "x": 0,
                "y": 0,
                "desc": "",
                "xpath": ""
            }
        """
        logging.info("保存坐标点: %s", point)
        self._points[point['name']] = point

    def add_func(self, func):
        """
        添加坐标点采集函数
        """
        self._func.append(func)

    def save_point(self):
        """
        保存坐标
        """
        self.to_top_swipe()
        self.d.sleep(2)

        for func in self._func:
            func()

        __tmp__ = {}
        for k, v in self._points.items():
            __tmp__[k] = v

        with open(self.point_path, "w", encoding="utf-8") as f:
            json.dump(__tmp__, f, ensure_ascii=False, indent=4)

    def get_point(self, info: dict):
        """
        获取坐标点
        info 数据结构
        {
            "name": "btn_mairu_kaiduo",
            "desc": "买入/开多",
            "xpath": '//*[@content-desc="买入/开多"]'

        """
        el = self.d.xpath(info['xpath']).get()
        x, y = el.center()
        self.add_point({
            "name": info['name'],
            "x": x,
            "y": y,
            "desc": info['desc'],
            "xpath": info['xpath'],
        })

        return x, y
