# 数据类定义
from enum import Enum, unique
from typing import Union

Number = Union[int, float]


class JsonBase:
    """@inner put all properties into a dict, remove properties which is None"""

    def to_dict(self, api_level=9):
        result = {}
        for key, value in self.__dict__.items():
            if value is not None:
                result[key] = value
        return result

    @classmethod
    def from_dict(cls, data: dict):
        obj = cls()
        for key, value in data.items():
            setattr(obj, key, value)
        return obj

    def __getitem__(self, key):
        """
        支持dict方式访问
        """
        return self.__dict__[key]

    def __str__(self):
        return str(self.__dict__)

    def __repr__(self):
        return repr(self.__dict__)


class Rect(JsonBase):
    """
    表示矩形的区域的位置
    """

    def __init__(self, left: Number = 0, right: Number = 0, top: Number = 0, bottom: Number = 0, display_id: Number = -1):
        """
        display_id = -1表示未指定屏幕, 未指定时, 操作使用当前设置的默认屏幕
        """
        self.left = int(left)
        self.right = int(right)
        self.top = int(top)
        self.bottom = int(bottom)
        self.displayId = display_id

    @classmethod
    def from_tuple(cls, left_up, right_bottom):
        return cls(left_up[0], right_bottom[0], left_up[1], right_bottom[1])

    def __str__(self):
        return "(left: %s, right: %s, top: %s, bottom: %s)" % (self.leftX, self.rightX, self.topY, self.bottomY)

    def get_size(self) -> (int, int):
        """
        @func 获取矩形区域的宽度和长度
        @return tuple类型,  (width, height)
        """
        return (self.right - self.left), (self.bottom - self.top)

    def get_center(self) -> (int, int):
        """
        @func 获取矩形区域的中心点坐标
        @return (center_x, center_y)
        """
        return int((self.leftX + self.rightX) / 2), int((self.topY + self.bottomY) / 2)

    @staticmethod
    def _convert_to_abs_value(scale, abs_max):
        if scale > 1:
            return scale
        else:
            return scale * abs_max

    def get_pos(self, x_offset: float, y_offset: float) -> (int, int):
        """
        @func 获取矩形区域内部指定位置的绝对坐标
        @param x_offset: 矩形区域内部x方向偏移, 支持相对距离[0, 1], 大于1表示绝对长度
        @param y_offset: 矩形区域内部y方向偏移, 支持相对距离[0, 1], 大于1表示绝对长度
        """
        width, height = self.get_size()
        x = self.left + self._convert_to_abs_value(x_offset, width)
        y = self.top + self._convert_to_abs_value(y_offset, height)
        return int(x), int(y)

    def iter(self):
        for item in [self.left, self.right, self.top, self.bottom]:
            yield item

    def __setitem__(self, key, value):
        """
        支持通过下标访问left,right,top,bottom
        """
        if key == 0:
            self.left = value
        elif key == 1:
            self.right = value
        elif key == 2:
            self.top = value
        elif key == 3:
            self.bottom = value
        else:
            raise IndexError(key)

    def __getitem__(self, index):
        if index == 0:
            return self.left
        if index == 1:
            return self.right
        if index == 2:
            return self.top
        if index == 3:
            return self.bottom
        raise IndexError(index)

    def __getattr__(self, item):
        if item == "leftX":
            return self.left
        elif item == 'rightX':
            return self.right
        elif item == 'topY':
            return self.top
        elif item == 'bottomY':
            return self.bottom
        else:
            raise AttributeError(item)

    def __setattr__(self, key, value):
        key_map = {
            'leftX': 'left',
            'rightX': 'right',
            'topY': 'top',
            'bottomY': 'bottom'
        }
        if key in key_map.keys():
            key = key_map[key]
        self.__dict__[key] = value

    @classmethod
    def from_dict(cls, data: dict):
        left = 0
        right = 0
        top = 0
        bottom = 0
        display_id = -1
        for item in data.keys():
            if item.lower().startswith("left"):
                left = data[item]
            elif item.lower().startswith("right"):
                right = data[item]
            elif item.lower().startswith("top"):
                top = data[item]
            elif item.lower().startswith("bottom"):
                bottom = data[item]
            elif item == "displayId":
                display_id = data["displayId"]
        return Rect(left, right, top, bottom, display_id)


class Point(JsonBase):
    """
    表示一个坐标点
    """

    def __init__(self, x: Number, y: Number, display_id: int = -1):
        self.X = int(x) if x > 1 else x
        self.Y = int(y) if y > 1 else y
        self.displayId = display_id

    def __getitem__(self, index):
        if index == 0:
            return self.X
        if index == 1:
            return self.Y
        raise IndexError(index)

    def iter(self):
        for item in [self.X, self.Y]:
            yield item

    def __setitem__(self, key, value):
        if key == 0:
            self.X = value
        if key == 1:
            self.Y = value
        raise IndexError(key)

    def __iter__(self):
        return (i for i in (self.X, self.Y))

    def __getattr__(self, item):
        if item == "x":
            return self.X
        elif item == 'y':
            return self.Y
        else:
            raise AttributeError(item)

    def __setattr__(self, key, value):
        if key.lower() == "x" or key.lower() == 'y':
            self.__dict__[key.upper()] = value
        else:
            super().__setattr__(key, value)

    def __str__(self):
        return "(%d, %d)" % (self.X, self.Y)

    def to_tuple(self):
        """
        @func 将Point对象转换为tuple类型, (x, y)
        """
        return self.X, self.Y

    def to_dict(self, api_level):
        if api_level >= 9:
            data = {"x": self.X, "y": self.Y}
            if self.displayId > 0:
                data["displayId"] = self.displayId
            return data
        else:
            return {"X": int(self.X), "Y": int(self.Y)}

    @staticmethod
    def from_dict(json):
        x = 0
        y = 0
        displayId = -1
        for item in json.keys():
            if item.lower() == "x":
                x = json[item]
            elif item.lower() == "y":
                y = json[item]
            elif item == "displayId":
                displayId = json[item]
        return Point(x, y, displayId)



@unique
class MatchPattern(Enum):
    """
    指定BY选择器的匹配模式, 例如BY.text("app_", MatchPattern.STARTS_WITH)
    ```
    from hypium.model import MatchPattern
    ```
    """
    EQUALS = 0
    CONTAINS = 1
    STARTS_WITH = 2
    ENDS_WITH = 3
    REGEXP = 4  # 正则匹配
    REGEXP_ICASE = 5  # 忽略大小写, 进行正则匹配


@unique
class WindowMode(Enum):
    """
    表示窗口状态, UiWindow.getWindowMode返回值类型
    ```
    from hypium.model import WindowMode
    ```
    """
    FULLSCREEN = 0
    PRIMARY = 1
    SECONDARY = 2
    FLOATING = 3


@unique
class ResizeDirection(Enum):
    """
    窗口大小调整方向, 调用UiWindow.resize时使用
    ```
    from hypium.model import ResizeDirection
    ```
    """
    LEFT = 0
    RIGHT = 1
    UP = 2
    DOWN = 3
    LEFT_UP = 4
    LEFT_DOWN = 5
    RIGHT_UP = 6
    RIGHT_DOWN = 7


class OSType:
    """
    @inner 操作系统类型
    ```
    from hypium.model import OSType
    ```
    """
    OHOS = "ohos"
    HMOS = "hmos"


class WindowFilter(JsonBase):
    """
    根据指定条件获取匹配的window
    ```
    from hypium.model import WindowFilter
    ```
    """

    def __init__(self, **kwargs):
        """
        @func 窗口选择器, 支持通过title, bundle_name, focused, actived选择窗口
        @param kwargs:
               title: str, 指定目标窗口标题
               bundle_name: str, 指定目标窗口包名
               focused: bool, 指定目标窗口是否获焦
               actived: bool, 指定目标窗口是否为当前活动窗口
        """
        self._data = {}
        for key, value in kwargs.items():
            if value is not None:
                self._data[key] = value

    def title(self, title: str):
        """
        @func 指定窗口标题
        @example filter = WindowFilter().title("音乐")
        """
        self._data["title"] = title
        return self

    def bundle_name(self, bundle_name: str):
        """
        @func 指定窗口所属的应用包名
        @example filter = WindowFilter().bundle_name("com.example.hap")
        """
        self._data["bundleName"] = bundle_name
        return self

    def focused(self, focused: bool = True):
        """
        @func 指定窗口是否获得焦点
        @example filter = WindowFilter().focused(True)
        """
        self._data["focused"] = focused
        return self

    def actived(self, actived: bool = True):
        """
        @func 指定窗口是否活动
        @example filter = WindowFilter().actived(True)
        """
        self._data["actived"] = actived
        return self

    def to_dict(self, api_level):
        return self._data


@unique
class DisplayRotation(Enum):
    """
    屏幕旋转角度，在UiDriver.setDisplayRotation中使用
    ```
    from hypium.model import DisplayRotation
    ```
    """
    ROTATION_0 = 0
    ROTATION_90 = 1
    ROTATION_180 = 2
    ROTATION_270 = 3


@unique
class MouseButton(Enum):
    """
    鼠标按键
    ```
    from hypium.model import MouseButton
    ```
    """
    MOUSE_BUTTON_LEFT = 0
    MOUSE_BUTTON_RIGHT = 1
    MOUSE_BUTTON_MIDDLE = 2


@unique
class KeyCode(Enum):
    """
    Openharmony键盘码
    ```
    from hypium.model import KeyCode
    ```
    """
    FN = 0  # 功能（Fn）键
    UNKNOWN = -1  # 未知按键
    HOME = 1  # 功能（Home）键
    BACK = 2  # 返回键
    MEDIA_PLAY_PAUSE = 10  # 多媒体键播放/暂停
    MEDIA_STOP = 11  # 多媒体键停止
    MEDIA_NEXT = 12  # 多媒体键下一首
    MEDIA_PREVIOUS = 13  # 多媒体键上一首
    MEDIA_REWIND = 14  # 多媒体键快退
    MEDIA_FAST_FORWARD = 15  # 多媒体键快进
    VOLUME_UP = 16  # 音量增加键
    VOLUME_DOWN = 17  # 音量减小键
    POWER = 18  # 电源键
    CAMERA = 19  # 拍照键
    VOLUME_MUTE = 22  # 扬声器静音键
    MUTE = 23  # 话筒静音键
    BRIGHTNESS_UP = 40  # 亮度调节按键调亮
    BRIGHTNESS_DOWN = 41  # 亮度调节按键调暗
    NUM_0 = 2000  # 按键’0’
    NUM_1 = 2001  # 按键’1’
    NUM_2 = 2002  # 按键’2’
    NUM_3 = 2003  # 按键’3’
    NUM_4 = 2004  # 按键’4’
    NUM_5 = 2005  # 按键’5’
    NUM_6 = 2006  # 按键’6’
    NUM_7 = 2007  # 按键’7’
    NUM_8 = 2008  # 按键’8’
    NUM_9 = 2009  # 按键’9’
    STAR = 2010  # 按键’*’
    POUND = 2011  # 按键’#’
    DPAD_UP = 2012  # 导航键向上
    DPAD_DOWN = 2013  # 导航键向下
    DPAD_LEFT = 2014  # 导航键向左
    DPAD_RIGHT = 2015  # 导航键向右
    DPAD_CENTER = 2016  # 导航键确定键
    A = 2017  # 按键’A’
    B = 2018  # 按键’B’
    C = 2019  # 按键’C’
    D = 2020  # 按键’D’
    E = 2021  # 按键’E’
    F = 2022  # 按键’F’
    G = 2023  # 按键’G’
    H = 2024  # 按键’H’
    I = 2025  # 按键’I’
    J = 2026  # 按键’J’
    K = 2027  # 按键’K’
    L = 2028  # 按键’L’
    M = 2029  # 按键’M’
    N = 2030  # 按键’N’
    O = 2031  # 按键’O’
    P = 2032  # 按键’P’
    Q = 2033  # 按键’Q’
    R = 2034  # 按键’R’
    S = 2035  # 按键’S’
    T = 2036  # 按键’T’
    U = 2037  # 按键’U’
    V = 2038  # 按键’V’
    W = 2039  # 按键’W’
    X = 2040  # 按键’X’
    Y = 2041  # 按键’Y’
    Z = 2042  # 按键’Z’
    COMMA = 2043  # 按键’,’
    PERIOD = 2044  # 按键’.’
    ALT_LEFT = 2045  # 左Alt键
    ALT_RIGHT = 2046  # 右Alt键
    SHIFT_LEFT = 2047  # 左Shift键
    SHIFT_RIGHT = 2048  # 右Shift键
    TAB = 2049  # Tab键
    SPACE = 2050  # 空格键
    SYM = 2051  # 符号修改器按键
    EXPLORER = 2052  # 浏览器功能键，此键用于启动浏览器应用程序。
    ENVELOPE = 2053  # 电子邮件功能键，此键用于启动电子邮件应用程序。
    ENTER = 2054  # 回车键
    DEL = 2055  # 退格键
    GRAVE = 2056  # 按键’`’
    MINUS = 2057  # 按键’-’
    EQUALS = 2058  # 按键’=’
    LEFT_BRACKET = 2059  # 按键’[’
    RIGHT_BRACKET = 2060  # 按键’]’
    BACKSLASH = 2061  # 按键’\’
    SEMICOLON = 2062  # 按键’;’
    APOSTROPHE = 2063  # 按键’‘’(单引号)
    SLASH = 2064  # 按键’/’
    AT = 2065  # 按键’@’
    PLUS = 2066  # 按键’+’
    MENU = 2067  # 菜单键
    PAGE_UP = 2068  # 向上翻页键
    PAGE_DOWN = 2069  # 向下翻页键
    ESCAPE = 2070  # ESC键
    FORWARD_DEL = 2071  # 删除键
    CTRL_LEFT = 2072  # 左Ctrl键
    CTRL_RIGHT = 2073  # 右Ctrl键
    CAPS_LOCK = 2074  # 大写锁定键
    SCROLL_LOCK = 2075  # 滚动锁定键
    META_LEFT = 2076  # 左元修改器键
    META_RIGHT = 2077  # 右元修改器键
    FUNCTION = 2078  # 功能键
    SYSRQ = 2079  # 系统请求/打印屏幕键
    BREAK = 2080  # Break/Pause键
    MOVE_HOME = 2081  # 光标移动到开始键
    MOVE_END = 2082  # 光标移动到末尾键
    INSERT = 2083  # 插入键
    FORWARD = 2084  # 前进键
    MEDIA_PLAY = 2085  # 多媒体键播放
    MEDIA_PAUSE = 2086  # 多媒体键暂停
    MEDIA_CLOSE = 2087  # 多媒体键关闭
    MEDIA_EJECT = 2088  # 多媒体键弹出
    MEDIA_RECORD = 2089  # 多媒体键录音
    F1 = 2090  # 按键’F1’
    F2 = 2091  # 按键’F2’
    F3 = 2092  # 按键’F3’
    F4 = 2093  # 按键’F4’
    F5 = 2094  # 按键’F5’
    F6 = 2095  # 按键’F6’
    F7 = 2096  # 按键’F7’
    F8 = 2097  # 按键’F8’
    F9 = 2098  # 按键’F9’
    F10 = 2099  # 按键’F10’
    F11 = 2100  # 按键’F11’
    F12 = 2101  # 按键’F12’
    NUM_LOCK = 2102  # 小键盘锁
    NUMPAD_0 = 2103  # 小键盘按键’0’
    NUMPAD_1 = 2104  # 小键盘按键’1’
    NUMPAD_2 = 2105  # 小键盘按键’2’
    NUMPAD_3 = 2106  # 小键盘按键’3’
    NUMPAD_4 = 2107  # 小键盘按键’4’
    NUMPAD_5 = 2108  # 小键盘按键’5’
    NUMPAD_6 = 2109  # 小键盘按键’6’
    NUMPAD_7 = 2110  # 小键盘按键’7’
    NUMPAD_8 = 2111  # 小键盘按键’8’
    NUMPAD_9 = 2112  # 小键盘按键’9’
    NUMPAD_DIVIDE = 2113  # 小键盘按键’/’
    NUMPAD_MULTIPLY = 2114  # 小键盘按键’*’
    NUMPAD_SUBTRACT = 2115  # 小键盘按键’-’
    NUMPAD_ADD = 2116  # 小键盘按键’+’
    NUMPAD_DOT = 2117  # 小键盘按键’.’
    NUMPAD_COMMA = 2118  # 小键盘按键’,’
    NUMPAD_ENTER = 2119  # 小键盘按键回车
    NUMPAD_EQUALS = 2120  # 小键盘按键’=’
    NUMPAD_LEFT_PAREN = 2121  # 小键盘按键’(’
    NUMPAD_RIGHT_PAREN = 2122  # 小键盘按键’)’
    VIRTUAL_MULTITASK = 2210  # 虚拟多任务键
    SLEEP = 2600  # 睡眠键
    ZENKAKU_HANKAKU = 2601  # 日文全宽/半宽键
    ND = 2602  # 102nd按键
    RO = 2603  # 日文Ro键
    KATAKANA = 2604  # 日文片假名键
    HIRAGANA = 2605  # 日文平假名键
    HENKAN = 2606  # 日文转换键
    KATAKANA_HIRAGANA = 2607  # 日语片假名/平假名键
    MUHENKAN = 2608  # 日文非转换键
    LINEFEED = 2609  # 换行键
    MACRO = 2610  # 宏键
    NUMPAD_PLUSMINUS = 2611  # 数字键盘上的加号/减号键
    SCALE = 2612  # 扩展键
    HANGUEL = 2613  # 日文韩语键
    HANJA = 2614  # 日文汉语键
    YEN = 2615  # 日元键
    STOP = 2616  # 停止键
    AGAIN = 2617  # 重复键
    PROPS = 2618  # 道具键
    UNDO = 2619  # 撤消键
    COPY = 2620  # 复制键
    OPEN = 2621  # 打开键
    PASTE = 2622  # 粘贴键
    FIND = 2623  # 查找键
    CUT = 2624  # 剪切键
    HELP = 2625  # 帮助键
    CALC = 2626  # 计算器特殊功能键，用于启动计算器应用程序
    FILE = 2627  # 文件按键
    BOOKMARKS = 2628  # 书签键
    NEXT = 2629  # 下一个按键
    PLAYPAUSE = 2630  # 播放/暂停键
    PREVIOUS = 2631  # 上一个按键
    STOPCD = 2632  # CD停止键
    CONFIG = 2634  # 配置键
    REFRESH = 2635  # 刷新键
    EXIT = 2636  # 退出键
    EDIT = 2637  # 编辑键
    SCROLLUP = 2638  # 向上滚动键
    SCROLLDOWN = 2639  # 向下滚动键
    NEW = 2640  # 新建键
    REDO = 2641  # 恢复键
    CLOSE = 2642  # 关闭键
    PLAY = 2643  # 播放键
    BASSBOOST = 2644  # 低音增强键
    PRINT = 2645  # 打印键
    CHAT = 2646  # 聊天键
    FINANCE = 2647  # 金融键
    CANCEL = 2648  # 取消键
    KBDILLUM_TOGGLE = 2649  # 键盘灯光切换键
    KBDILLUM_DOWN = 2650  # 键盘灯光调亮键
    KBDILLUM_UP = 2651  # 键盘灯光调暗键
    SEND = 2652  # 发送键
    REPLY = 2653  # 答复键
    FORWARDMAIL = 2654  # 邮件转发键
    SAVE = 2655  # 保存键
    DOCUMENTS = 2656  # 文件键
    VIDEO_NEXT = 2657  # 下一个视频键
    VIDEO_PREV = 2658  # 上一个视频键
    BRIGHTNESS_CYCLE = 2659  # 背光渐变键
    BRIGHTNESS_ZERO = 2660  # 亮度调节为0键
    DISPLAY_OFF = 2661  # 显示关闭键
    BTN_MISC = 2662  # 游戏手柄上的各种按键
    GOTO = 2663  # 进入键
    INFO = 2664  # 信息查看键
    PROGRAM = 2665  # 程序键
    PVR = 2666  # 个人录像机(PVR)键
    SUBTITLE = 2667  # 字幕键
    FULL_SCREEN = 2668  # 全屏键
    KEYBOARD = 2669  # 键盘
    ASPECT_RATIO = 2670  # 屏幕纵横比调节键
    PC = 2671  # 端口控制键
    TV = 2672  # TV键
    TV2 = 2673  # TV键2
    VCR = 2674  # 录像机开启键
    VCR2 = 2675  # 录像机开启键2
    SAT = 2676  # SIM卡应用工具包（SAT）键
    CD = 2677  # CD键
    TAPE = 2678  # 磁带键
    TUNER = 2679  # 调谐器键
    PLAYER = 2680  # 播放器键
    DVD = 2681  # DVD键
    AUDIO = 2682  # 音频键
    VIDEO = 2683  # 视频键
    MEMO = 2684  # 备忘录键
    CALENDAR = 2685  # 日历键
    RED = 2686  # 红色指示器
    GREEN = 2687  # 绿色指示器
    YELLOW = 2688  # 黄色指示器
    BLUE = 2689  # 蓝色指示器
    CHANNELUP = 2690  # 频道向上键
    CHANNELDOWN = 2691  # 频道向下键
    LAST = 2692  # 末尾键
    RESTART = 2693  # 重启键
    SLOW = 2694  # 慢速键
    SHUFFLE = 2695  # 随机播放键
    VIDEOPHONE = 2696  # 可视电话键
    GAMES = 2697  # 游戏键
    ZOOMIN = 2698  # 放大键
    ZOOMOUT = 2699  # 缩小键
    ZOOMRESET = 2700  # 缩放重置键
    WORDPROCESSOR = 2701  # 文字处理键
    EDITOR = 2702  # 编辑器键
    SPREADSHEET = 2703  # 电子表格键
    GRAPHICSEDITOR = 2704  # 图形编辑器键
    PRESENTATION = 2705  # 演示文稿键
    DATABASE = 2706  # 数据库键标
    NEWS = 2707  # 新闻键
    VOICEMAIL = 2708  # 语音信箱
    ADDRESSBOOK = 2709  # 通讯簿
    MESSENGER = 2710  # 通信键
    BRIGHTNESS_TOGGLE = 2711  # 亮度切换键
    SPELLCHECK = 2712  # AL拼写检查
    COFFEE = 2713  # 终端锁/屏幕保护程序
    MEDIA_REPEAT = 2714  # 媒体循环键
    IMAGES = 2715  # 图像键
    BUTTONCONFIG = 2716  # 按键配置键
    TASKMANAGER = 2717  # 任务管理器
    JOURNAL = 2718  # 日志按键
    CONTROLPANEL = 2719  # 控制面板键
    APPSELECT = 2720  # 应用程序选择键
    SCREENSAVER = 2721  # 屏幕保护程序键
    ASSISTANT = 2722  # 辅助键
    KBD_LAYOUT_NEXT = 2723  # 下一个键盘布局键
    BRIGHTNESS_MIN = 2724  # 最小亮度键
    BRIGHTNESS_MAX = 2725  # 最大亮度键
    KBDINPUTASSIST_PREV = 2726  # 键盘输入Assist_Previous
    KBDINPUTASSIST_NEXT = 2727  # 键盘输入Assist_Next
    KBDINPUTASSIST_PREVGROUP = 2728  # 键盘输入Assist_Previous
    KBDINPUTASSIST_NEXTGROUP = 2729  # 键盘输入Assist_Next
    KBDINPUTASSIST_ACCEPT = 2730  # 键盘输入Assist_Accept
    KBDINPUTASSIST_CANCEL = 2731  # 键盘输入Assist_Cancel
    FRONT = 2800  # 挡风玻璃除雾器开关
    SETUP = 2801  # 设置键
    WAKE_UP = 2802  # 唤醒键
    SENDFILE = 2803  # 发送文件按键
    DELETEFILE = 2804  # 删除文件按键
    XFER = 2805  # 文件传输(XFER)按键
    PROG1 = 2806  # 程序键1
    PROG2 = 2807  # 程序键2
    MSDOS = 2808  # MS-DOS键（微软磁盘操作系统
    SCREENLOCK = 2809  # 屏幕锁定键
    DIRECTION_ROTATE_DISPLAY = 2810  # 方向旋转显示键
    CYCLEWINDOWS = 2811  # Windows循环键
    COMPUTER = 2812  # 按键
    EJECTCLOSECD = 2813  # 弹出CD键
    ISO = 2814  # ISO键
    MOVE = 2815  # 移动键
    F13 = 2816  # 按键’F13’
    F14 = 2817  # 按键’F14’
    F15 = 2818  # 按键’F15’
    F16 = 2819  # 按键’F16’
    F17 = 2820  # 按键’F17’
    F18 = 2821  # 按键’F18’
    F19 = 2822  # 按键’F19’
    F20 = 2823  # 按键’F20’
    F21 = 2824  # 按键’F21’
    F22 = 2825  # 按键’F22’
    F23 = 2826  # 按键’F23’
    F24 = 2827  # 按键’F24’
    PROG3 = 2828  # 程序键3
    PROG4 = 2829  # 程序键4
    DASHBOARD = 2830  # 仪表板
    SUSPEND = 2831  # 挂起键
    HP = 2832  # 高阶路径键
    SOUND = 2833  # 音量键
    QUESTION = 2834  # 疑问按键
    CONNECT = 2836  # 连接键
    SPORT = 2837  # 运动按键
    SHOP = 2838  # 商城键
    ALTERASE = 2839  # 交替键
    SWITCHVIDEOMODE = 2841  # 在可用视频之间循环输出（监视器/LCD/TV输出/等）
    BATTERY = 2842  # 电池按键
    BLUETOOTH = 2843  # 蓝牙按键
    WLAN = 2844  # 无线局域网
    UWB = 2845  # 超宽带（UWB）
    WWAN_WIMAX = 2846  # WWANWiMAX按键
    RFKILL = 2847  # 控制所有收音机的键
    CHANNEL = 3001  # 向上频道键
    BTN_0 = 3100  # 按键0
    BTN_1 = 3101  # 按键1
    BTN_2 = 3102  # 按键2
    BTN_3 = 3103  # 按键3
    BTN_4 = 3104  # 按键4
    BTN_5 = 3105  # 按键5
    BTN_6 = 3106  # 按键6
    BTN_7 = 3107  # 按键7
    BTN_8 = 3108  # 按键8
    BTN_9 = 3109  # 按键9


class UiParam:
    """
    Ui操作控制相关的常量
    ```
    from hypium.model import UiParam
    ```
    """
    NORMAL = "normal"
    LONG = "long"
    DOUBLE = "double"
    LEFT = "LEFT"
    RIGHT = "RIGHT"
    UP = "UP"
    DOWN = "DOWN"
    BEGIN = "begin"
    END = "end"
    SLOW = "slow"
    FAST = "fast"
    TOP = "top"
    BOTTOM = "bottom"
    CENTER = "center"
    DIAGONAL = "diagonal"
    HORIZONTAL = "horizontal"


class GestureStep:
    """@inner 存储手势中的每个步骤，该类不能直接使用，通过Gesture类使用"""

    def __init__(self, pos: tuple, gesture_type: str, interval: float):
        self.pos = pos[0], pos[1]
        if interval is None:
            self.interval = None
        else:
            self.interval = int(interval * 1000)
        self.type = gesture_type


class DeviceType:
    """
    设备类型
    ```
    from hypium.model import DeviceType
    ```
    """
    # 2in1
    TWO_IN_ONE = '2in1'
    # PC
    PC = "tablet"
    # 平板
    TABLET = "tablet"
    # 手机
    PHONE = "phone"
    # 穿戴设备
    WEARABLE = "wearable"

    @staticmethod
    def is_tablet_category(device_type):
        if device_type == DeviceType.TWO_IN_ONE or device_type == DeviceType.TABLET:
            return True
        else:
            return False


class AppState:
    """应用程序状态常量"""
    INIT = 0  # 初始化状态，应用正在初始化
    READY = 1  # 就绪状态，应用已初始化完毕
    FOREGROUND = 2  # 前台状态，应用位于前台
    FOCUS = 3  # 获焦状态。（预留状态，当前暂不支持）
    BACKGROUND = 4  # 后台状态，应用位于后台
    EXIT = 5  # 退出状态，应用已退出


class TouchPadSwipeOptions(JsonBase):
    """
    触摸板滑动选择器
    """

    def __init__(self, **kwargs):
        """
        @func 触摸板滑动选择器
        @param kwargs:
            stay:bool 滑动结束以后是否停留一秒，默认值为FALSE
            speed:int  触摸板多指滑动速度，默认值2000 速度范围200 - 40000，超过范围则取值为默认值（2000）
        """
        self._data = {}
        for key, value in kwargs.items():
            if value is not None:
                self._data[key] = value

    def to_dict(self, api_level=9):
        result = {}
        for item in self._data.items():
            key, value = item
            if value is not None:
                result[key] = value
        return result

    def stay(self, stay: bool = True):
        """
        @func 指定在操作结束位置是否停留
        @param stay: 滑动结束以后是否停留一秒，默认值为FALSE
        @example option = TouchPadSwipeOptions().stay(True)
        """
        self._data["stay"] = stay
        return self

    def speed(self, speed: int):
        """
        @func 指定速度
        @param speed: 触摸板多指滑动速度，默认值2000 速度范围200 - 40000，超过范围则取值为默认值（2000）
        @example option = TouchPadSwipeOptions().speed(2000)
        """
        self._data["speed"] = speed
        return self


class InputDevice:
    TOUCHSCREEN = 'touchscreen'
    MOUSE = 'mouse'
    PEN = 'pen'


class InputTextMode(JsonBase):
    """
    输入模式选择器
    """

    def __init__(self, **kwargs):
        """
        @func 输入模式选择器
        @param kwargs:
               paste:bool 是否指定以复制粘贴方式输入
               addition:bool  是否以追加的方式进行输入
        """
        self._data = {}
        for key, value in kwargs.items():
            if value is not None:
                self._data[key] = value

    def paste(self, paste: bool = False):
        """
        @func 输入文本时是否指定以复制粘贴方式输入。
              1.当输入文本中包含中文、特殊字符或文本长度超过200字符时，无论该参数取值为何，均以复制粘贴方式输入。
              2.在智能穿戴设备中，该接口不支持以复制粘贴方式输入。
        @example # 通过剪切板粘贴的方式输入文本
                 driver.input_text(BY.type("TextInput"), "123456789", mode=InputTextMode().paste(True))

        """
        self._data["paste"] = paste
        return self

    def addition(self, addition: bool = False):
        """
            @func 	输入文本时是否以追加的方式进行输入
            @example # 追加输入文本
                     driver.input_text(BY.type("TextInput"), "测试", mode=InputTextMode().addition(True))
        """
        self._data["addition"] = addition
        return self

    def to_dict(self, api_level=9):
        return self._data
