import cv2
from scipy.signal import argrelmin
from skimage.metrics import structural_similarity

from mower.data import agent_list
from mower.models import Digtal
from mower.solvers.navigation import NavigationSolver
from mower.utils import config
from mower.utils import typealias as tp
from mower.utils.character_recognize import match_portrait
from mower.utils.graph import SceneGraphSolver
from mower.utils.image import cmatch, cropimg, loadres, thres2
from mower.utils.log import logger
from mower.utils.recognize import Scene

profession_pos = {
    "PIONEER": ((30, 150), (40, 160)),
    "WARRIOR": ((30, 263), (40, 273)),
    "TANK": ((30, 376), (40, 386)),
    "SNIPER": ((30, 489), (40, 499)),
    "CASTER": ((30, 602), (40, 612)),
    "MEDIC": ((30, 715), (40, 725)),
    "SUPPORT": ((30, 830), (40, 840)),
    "SPECIAL": ((30, 944), (40, 954)),
}
skill_pos = (
    ((1237, 390), (1252, 405)),
    ((1409, 390), (1424, 405)),
    ((1582, 390), (1597, 405)),
)


class ChooseSupportSolver(SceneGraphSolver):
    "选择助战干员"

    def run(
        self,
        opers=[],
        level_name: str = None,
    ):
        """
        Args:
            opers: 包含name和skill,若传入多个干员会选择其中练度最高的
            level_name: 关卡名称,用于导航,可不传
        """
        self.opers_skill = {}
        if len(opers) == 0:
            return True
        for i in range(len(opers) - 1, -1, -1):  # 改变group的结构
            op = opers[i]
            if "opers" in op:
                opers += op["opers"]
                del opers[i]
        logger.debug(f"选择助战干员: {opers}")
        for op in opers:
            self.opers_skill[op["name"]] = op["skill"]
        logger.info(f"Start: 选择助战干员{self.opers_skill}")
        self.min_progression = (
            config.conf.support.elite * 100 + config.conf.support.level
        )  # 最低练度要求
        self.retry_times = config.conf.support.refresh  # 刷新次数
        self.level_name = level_name
        self.profession = agent_list[opers[0]["name"]]["profession"]  # 目标干员职业
        self.matched = False  # 是否匹配过干员
        self.swiped = False  # 是否滑动过
        self.success = False  # 是否成功选择到干员
        self.skill = None  # 选中干员需要的技能
        super().run()
        return self.success

    def check_profession_focus(self) -> bool:
        img = cropimg(config.recog.img, profession_pos[self.profession])
        res = loadres("fight/profession_not_be_choosen")
        return not cmatch(img, res, 70)

    def get_oper_pos(self):
        if not self.swiped:
            img = cropimg(config.recog.gray, ((0, 908), (1839, 983)))
        else:
            img = cropimg(config.recog.gray, ((1600, 908), (1920, 983)))
        res = loadres("fight/choose", True)
        result = cv2.matchTemplate(img, res, cv2.TM_SQDIFF_NORMED)[0]
        threshold = 0.1
        match = []
        for i in argrelmin(result, order=100)[0]:
            if result[i] < threshold:
                if self.swiped:
                    match.append(((i + 1600, 500), (i + 1794, 625)))
                else:
                    match.append(((i, 500), (i + 194, 625)))
        logger.debug(match)
        match_opers = dict(match_portrait(config.recog.gray, segment=match))
        result = {}
        for op in self.opers_skill:
            if op in match_opers:
                result[op] = match_opers[op]
        return result

    def get_agent_elite(self, scope: tp.Scope) -> int:
        "获取干员精英等级"
        img = cropimg(
            config.recog.gray,
            (
                (scope[0][0] + 55, scope[0][1] + 140),
                (scope[1][0] - 54, scope[1][1] + 80),
            ),
        )
        img = thres2(img, 200)
        e1 = loadres("fight/elite1", True)
        e2 = loadres("fight/elite2", True)
        ssim1 = structural_similarity(img, e1)
        ssim2 = structural_similarity(img, e2)
        if ssim2 > 0.8:
            return 2
        elif ssim1 > 0.8:
            return 1
        else:
            return 0

    def number(self, scope: tp.Scope, height: int, thres: int) -> int:
        "数字识别"
        img = cropimg(config.recog.gray, scope)
        default_height = 25
        if height != default_height:
            scale = 25 / height
            img = cv2.resize(img, None, None, scale, scale)
        img = thres2(img, thres)
        contours, _ = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        rect = [cv2.boundingRect(c) for c in contours]
        rect.sort(key=lambda c: c[0])

        value = 0

        for x, y, w, h in rect:
            digit = cropimg(img, ((x, y), (x + w, y + h)))
            digit = cv2.copyMakeBorder(
                digit, 10, 10, 10, 10, cv2.BORDER_CONSTANT, None, (0,)
            )
            score = []
            for i in range(10):
                im = Digtal().secret_front[i]
                result = cv2.matchTemplate(digit, im, cv2.TM_SQDIFF_NORMED)
                min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
                score.append(min_val)
            value = value * 10 + score.index(min(score))

        return value

    def get_agent_progression(self, scope: tp.Scope) -> int:
        "获取干员练度"
        elite = self.get_agent_elite(scope)
        level = self.number(
            (
                (scope[0][0] + 125, scope[0][1] + 220),
                (scope[1][0] - 19, scope[1][1] + 130),
            ),
            24,
            180,
        )
        logger.debug(f"elite: {elite}, level: {level}")
        return elite * 100 + level

    def check_agent(self, cur_opers: dict):
        "检查干员是否符合要求"
        if cur_opers is None:
            return None
        result = None
        max_progression = self.min_progression - 1
        for op in cur_opers:
            progression = self.get_agent_progression(cur_opers[op])
            if progression > max_progression:
                max_progression = progression
                result = cur_opers[op]
                self.skill = self.opers_skill[op]
        return result

    def transition(self):
        if (scene := self.scene()) == Scene.OPERATOR_SUPPORT:
            if self.retry_times < 0:
                self.scene_graph_step(Scene.OPERATOR_SELECT)
            if not self.check_profession_focus():
                self.tap(profession_pos[self.profession])
            if self.matched:
                if self.find("fight/refresh"):
                    self.swipe_noinertia((800, 800), (150, 0))
                    self.tap_element("fight/refresh")
                    self.retry_times -= 1
                    self.matched = False
                else:
                    self.sleep(0.5)
            else:
                if result := self.check_agent(self.get_oper_pos()):
                    self.tap(result)
                elif not self.swiped:
                    self.swiped = True
                    self.swipe_noinertia((800, 800), (-150, 0))
                else:
                    self.matched = True
                    self.swiped = False

        elif scene == Scene.OPERATOR_SUPPORT_AGENT:
            if not self.find(
                "choose_agent/support_skill_be_choosen", scope=skill_pos[self.skill - 1]
            ):
                self.tap(skill_pos[self.skill - 1])
            else:
                self.tap_element("fight/use")
                self.success = True

        elif scene == Scene.OPERATOR_SELECT:
            if self.success:
                return True
            elif self.retry_times < 0:
                logger.warning("选择助战干员失败,请降低练度要求或添加好友")
                return True
            else:
                self.ctap((1660, 315))
        elif scene in self.waiting_scene:
            self.waiting_solver()
        else:
            NavigationSolver().run(self.level_name, mode="copy")
