import ctypes
import ctypes.util
import json
import os
import pathlib
import platform
from typing import Optional, Union

from .utils import JSON, InstanceOptionType, StaticOptionType


class Asst:
    CallBackType = ctypes.CFUNCTYPE(
        None, ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p
    )
    """
    回调函数，使用实例可参照 my_callback

    :params:
        ``param1 message``: 消息类型
        ``param2 details``: json string
        ``param3 arg``:     自定义参数
    """

    @staticmethod
    def load(
        path: Union[pathlib.Path, str],
        incremental_path: Optional[Union[pathlib.Path, str]] = None,
        user_dir: Optional[Union[pathlib.Path, str]] = None,
    ) -> bool:
        """
        加载 dll 及资源

        :params:
            ``path``:    DLL及资源所在文件夹路径
            ``incremental_path``:   增量资源所在文件夹路径
            ``user_dir``:   用户数据（日志、调试图片等）写入文件夹路径
        """

        platform_values = {
            "windows": {"libpath": "MaaCore.dll", "environ_var": "PATH"},
            "darwin": {
                "libpath": "libMaaCore.dylib",
                "environ_var": "DYLD_LIBRARY_PATH",
            },
            "linux": {"libpath": "libMaaCore.so", "environ_var": "LD_LIBRARY_PATH"},
        }
        lib_import_func = None

        platform_type = platform.system().lower()
        if platform_type == "windows":
            lib_import_func = ctypes.WinDLL
        else:
            lib_import_func = ctypes.CDLL

        Asst.__libpath = pathlib.Path(path) / platform_values[platform_type]["libpath"]
        try:
            os.environ[
                platform_values[platform_type]["environ_var"]
            ] += os.pathsep + str(path)
        except KeyError:
            os.environ[platform_values[platform_type]["environ_var"]] = (
                os.pathsep + str(path)
            )

        try:
            Asst.__lib = lib_import_func(str(Asst.__libpath))
        except OSError:
            Asst.__libpath = ctypes.util.find_library("MaaCore")
            Asst.__lib = lib_import_func(str(Asst.__libpath))

        Asst.__set_lib_properties()

        ret: bool = True
        if user_dir:
            ret &= Asst.__lib.AsstSetUserDir(str(user_dir).encode("utf-8"))

        ret &= Asst.__lib.AsstLoadResource(str(path).encode("utf-8"))
        if incremental_path:
            ret &= Asst.__lib.AsstLoadResource(str(incremental_path).encode("utf-8"))

        return ret

    def __init__(self, callback: CallBackType = None, arg=None):
        """
        :params:
            ``callback``:   回调函数
            ``arg``:        自定义参数
        """
        self.__callback = callback
        if callback:
            self.__ptr = Asst.__lib.AsstCreateEx(callback, arg)
        else:
            self.__ptr = Asst.__lib.AsstCreate()

    def __del__(self):
        Asst.__lib.AsstDestroy(self.__ptr)
        self.__ptr = None

    def set_instance_option(self, option_type: InstanceOptionType, option_value: str):
        """
        设置额外配置
        参见${MaaAssistantArknights}/src/MaaCore/Assistant.cpp#set_instance_option

        :params:
            ``externa_config``: 额外配置类型
            ``config_value``:   额外配置的值

        :return: 是否设置成功
        """
        return Asst.__lib.AsstSetInstanceOption(
            self.__ptr, int(option_type), option_value.encode("utf-8")
        )

    def set_static_option(option_type: StaticOptionType, option_value: str):
        """
        设置进程级参数
        参见${MaaAssistantArknights}/src/MaaCore/Assistant.cpp#set_static_option

        :params:
            ``option_type``:    进程级参数类型
            ``option_value``:   进程级参数的值

        :return: 是否设置成功
        """
        return Asst.__lib.AsstSetStaticOption(
            int(option_type), option_value.encode("utf-8")
        )

    def connect(self, adb_path: str, address: str, config: str = "General"):
        """
        连接设备

        :params:
            ``adb_path``:       adb 程序的路径
            ``address``:        adb 地址+端口
            ``config``:         adb 配置，可参考 resource/config.json

        :return: 是否连接成功
        """
        return Asst.__lib.AsstConnect(
            self.__ptr,
            adb_path.encode("utf-8"),
            address.encode("utf-8"),
            config.encode("utf-8"),
        )

    def get_image(self, size: int) -> bytes | None:
        """
        获取上次截图
        :params:
            ``size``:  图像字节数, 如 1280*720*3

        : return: 成功时图像的字节; 失败时 None
        """
        buffer_type = ctypes.c_byte * size
        buffer = buffer_type()
        buffer.value = b"\000" * size
        if (got := Asst.__lib.AsstGetImage(self.__ptr, buffer, size)) and got > 0:
            return bytes(buffer)
        else:
            return None

    def set_connection_extras(name: str, extras: JSON):
        """
        连接模拟器端的Extras

        :params:
            ``name``:           Extras名称
            ``extras``:         Extras配置
        """
        Asst.__lib.AsstSetConnectionExtras(
            name.encode("utf-8"), json.dumps(extras, ensure_ascii=False).encode("utf-8")
        )

    TaskId = int

    def append_task(self, type_name: str, params: JSON = {}) -> TaskId:
        """
        添加任务

        :params:
            ``type_name``:  任务类型，请参考 docs/集成文档.md
            ``params``:     任务参数，请参考 docs/集成文档.md

        :return: 任务 ID, 可用于 set_task_params 接口
        """
        return Asst.__lib.AsstAppendTask(
            self.__ptr,
            type_name.encode("utf-8"),
            json.dumps(params, ensure_ascii=False).encode("utf-8"),
        )

    def set_task_params(self, task_id: TaskId, params: JSON) -> bool:
        """
        动态设置任务参数

        :params:
            ``task_id``:  任务 ID, 使用 append_task 接口的返回值
            ``params``:   任务参数，同 append_task 接口，请参考 docs/集成文档.md

        :return: 是否成功
        """
        return Asst.__lib.AsstSetTaskParams(
            self.__ptr, task_id, json.dumps(params, ensure_ascii=False).encode("utf-8")
        )

    def start(self) -> bool:
        """
        开始任务

        :return: 是否成功
        """
        return Asst.__lib.AsstStart(self.__ptr)

    def stop(self) -> bool:
        """
        停止并清空所有任务

        :return: 是否成功
        """
        return Asst.__lib.AsstStop(self.__ptr)

    def running(self) -> bool:
        """
        是否正在运行

        :return: 是否正在运行
        """
        return Asst.__lib.AsstRunning(self.__ptr)

    @staticmethod
    def log(level: str, message: str) -> None:
        """
        打印日志

        :params:
            ``level``:      日志等级标签
            ``message``:    日志内容
        """

        Asst.__lib.AsstLog(level.encode("utf-8"), message.encode("utf-8"))

    def get_version(self) -> str:
        """
        获取DLL版本号

        : return: 版本号
        """
        return Asst.__lib.AsstGetVersion().decode("utf-8")

    @staticmethod
    def __set_lib_properties():
        Asst.__lib.AsstSetUserDir.restype = ctypes.c_bool
        Asst.__lib.AsstSetUserDir.argtypes = (ctypes.c_char_p,)

        Asst.__lib.AsstLoadResource.restype = ctypes.c_bool
        Asst.__lib.AsstLoadResource.argtypes = (ctypes.c_char_p,)

        Asst.__lib.AsstSetStaticOption.restype = ctypes.c_bool
        Asst.__lib.AsstSetStaticOption.argtypes = (
            ctypes.c_int,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstSetConnectionExtras.restype = ctypes.c_void_p
        Asst.__lib.AsstSetConnectionExtras.argtypes = (
            ctypes.c_char_p,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstGetImage.restype = ctypes.c_uint64
        Asst.__lib.AsstGetImage.argtypes = (
            ctypes.c_void_p,
            ctypes.c_void_p,
            ctypes.c_uint64,
        )

        Asst.__lib.AsstCreate.restype = ctypes.c_void_p
        Asst.__lib.AsstCreate.argtypes = ()

        Asst.__lib.AsstCreateEx.restype = ctypes.c_void_p
        Asst.__lib.AsstCreateEx.argtypes = (
            ctypes.c_void_p,
            ctypes.c_void_p,
        )

        Asst.__lib.AsstDestroy.argtypes = (ctypes.c_void_p,)

        Asst.__lib.AsstSetInstanceOption.restype = ctypes.c_bool
        Asst.__lib.AsstSetInstanceOption.argtypes = (
            ctypes.c_void_p,
            ctypes.c_int,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstConnect.restype = ctypes.c_bool
        Asst.__lib.AsstConnect.argtypes = (
            ctypes.c_void_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstAsyncConnect.restype = ctypes.c_int
        Asst.__lib.AsstAsyncConnect.argtypes = (
            ctypes.c_void_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
            ctypes.c_bool,
        )

        Asst.__lib.AsstAppendTask.restype = ctypes.c_int
        Asst.__lib.AsstAppendTask.argtypes = (
            ctypes.c_void_p,
            ctypes.c_char_p,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstSetTaskParams.restype = ctypes.c_bool
        Asst.__lib.AsstSetTaskParams.argtypes = (
            ctypes.c_void_p,
            ctypes.c_int,
            ctypes.c_char_p,
        )

        Asst.__lib.AsstStart.restype = ctypes.c_bool
        Asst.__lib.AsstStart.argtypes = (ctypes.c_void_p,)

        Asst.__lib.AsstStop.restype = ctypes.c_bool
        Asst.__lib.AsstStop.argtypes = (ctypes.c_void_p,)

        Asst.__lib.AsstRunning.restype = ctypes.c_bool
        Asst.__lib.AsstRunning.argtypes = (ctypes.c_void_p,)

        Asst.__lib.AsstGetVersion.restype = ctypes.c_char_p

        Asst.__lib.AsstLog.restype = None
        Asst.__lib.AsstLog.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
