from datetime import datetime

import cv2
import numpy as np

from mower.models import Digtal
from mower.solvers.infra.base_mixin import BaseMixin
from mower.solvers.infra.enter_room import EnterRoomSolver
from mower.solvers.infra.filter import RIIC_Filter
from mower.utils import config
from mower.utils.character_recognize import (
    match_portrait,
    operator_room_select,
)
from mower.utils.graph import SceneGraphSolver
from mower.utils.image import cropimg
from mower.utils.log import logger
from mower.utils.scene import Scene
from mower.utils.vector import sa, va


class RIIC_ChooseSolver(SceneGraphSolver, BaseMixin):
    def run(self, room: str, agent_list: list, wait_time: float = 0) -> bool:
        self.room = room
        self.agent_list = agent_list
        self.wait_time = wait_time
        self.wait_choose = {}
        self.choosed = []
        self.cleard = False
        self.filter = RIIC_Filter()
        self.swipe_time = 0
        self.first_filter = True
        self.in_adjust = False
        self.tmp_data = {}
        self.tmp_checked = {}
        self.start_time = datetime.now()
        self.success = False
        self.check_room()
        try:
            return super().run()
        except ValueError as e:
            logger.error(e.__traceback__)
            return False

    def riic_agent_choose(self):
        if (
            len(list(set(self.choosed) & set(self.agent_list))) == len(self.agent_list)
            and len(self.agent_list) > 1
        ):
            if self.check_agent_order():
                return True
            else:
                return

        if self.first_filter:
            if self.first_filter:
                self.filter.run(技能=False)
                self.first_filter = False
            choosed_pos = self.get_agent_pos()
            if choosed_pos:
                if len(self.agent_list) > 1:
                    self.tap_element("choose_agent/clear", interval=0.1)
                else:
                    tap_pos = choosed_pos.popitem()[1][0]
                    self.tap(self.tap(va(tap_pos, (100, 100))), interval=0.1)
            self.first_filter = False
            return

        if len(self.wait_choose) > 0:
            logger.debug(f"待选名单{self.wait_choose}")
            choosed_pos = self.get_agent_pos()
            for name, pos in list(self.wait_choose.items()):
                if name in list(choosed_pos):
                    if len(self.agent_list) == 1:
                        return True
                    self.choosed.append(name)
                    del self.wait_choose[name]
                else:
                    logger.info(f"tap:{name},{pos}")
                    self.tap(va(pos[0], (100, 100)), interval=0.2)
            logger.debug(f"待选名单{self.wait_choose}")
            return
        agents = dict(operator_room_select(config.recog.img))
        if set(agents.keys()).issubset(self.tmp_data.keys()):
            self.swipe_time = self.swipe_time + 1
            if self.swipe_time > 3:
                raise ValueError("选择干员失败 滑动次数过多")
        else:
            self.swipe_time = 0

        self.tmp_data = agents
        logger.info(agents)
        intersection = list(set(agents.keys()) & set(self.agent_list))

        if intersection:
            for i in intersection:
                if i not in self.choosed:
                    self.wait_choose[i] = agents[i]
        if self.wait_choose:
            return
        else:
            # 加一个判断最右
            self.swipe_noinertia((1000, 540), (-1900, 0), interval=0.1)

    def check_agent_order(self):
        choosed_agents = self.get_agent_pos(no_number=False)
        logger.info(f"{choosed_agents}{self.agent_list}")
        if len(choosed_agents) < len(self.agent_list):
            if self.in_adjust is False:
                self.filter.run(未进驻=None)
                return False

            agents = dict(operator_room_select(config.recog.img))
            for i in self.agent_list:
                self.tap(va(agents[i][0], (100, 0)), interval=0)
            # 盲点结束等一下
            self.sleep()
            return False

        elif len(choosed_agents) == len(self.agent_list):
            for i in self.agent_list:
                if choosed_agents[i][0] != (self.agent_list.index(i)) + 1:
                    self.tap_element("choose_agent/clear", interval=0.5)
                    self.in_adjust = True
                    return False
        return True

    def check_with_number(self, img, height=60):
        _, im_w, _ = img.shape
        img = cropimg(img, ((0, 0), (im_w, 100)))
        kernel = np.ones((9, 9), np.uint8)
        img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
        img = cv2.inRange(img, (0, 0, 200), (225, 15, 255))

        img = cv2.erode(img, kernel)
        img = cv2.dilate(img, kernel)
        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,)
            )

            if digit.shape[0] < 65 or digit.shape[1] < 40:
                continue
            score = []
            kernel = np.ones((2, 2), np.uint8)
            for i in range(10):
                im = Digtal().secret_front[i]
                default_height = 25
                if height and height != default_height:
                    scale = default_height / height
                    digit_1 = cv2.resize(digit, None, None, scale, scale)

                im = cv2.dilate(im, kernel=kernel)
                result = cv2.matchTemplate(digit_1, im, cv2.TM_SQDIFF_NORMED)
                min_val, _, _, _ = cv2.minMaxLoc(result)
                score.append(min_val)
            value = value * 10 + score.index(min(score))
        return value

    def get_agent_pos(self, no_number=True):
        res = {}
        img = cv2.cvtColor(config.recog.img, cv2.COLOR_RGB2HSV)
        img = cv2.inRange(img, (90, 200, 120), (200, 255, 255))
        contours, _ = cv2.findContours(img, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        segments = []
        for contour in contours:
            if cv2.contourArea(contour) < 40000 or cv2.contourArea(contour) > 100000:
                continue
            x, y, w, h = cv2.boundingRect(contour)
            segments.append(((x, y + 100), (x + w, y + h - 200)))
        logger.debug(f"segments:{segments}")

        opear = match_portrait(config.recog.gray, segment=segments)
        if no_number:
            return list(dict(opear).keys())
        if len(self.agent_list) > 1:
            for i in opear:
                res[i[0]] = [
                    self.check_with_number(
                        cropimg(config.recog.img, sa([i[1][0], i[1][1]], (0, -100)))
                    ),
                    i[1],
                ]
        else:
            for i in opear:
                res[i[0]] = i[1]

        return res

    def check_wait_time(self) -> float:
        return max(
            self.wait_time - (datetime.now() - self.start_time).total_seconds(), 0
        )

    def check_room(self):
        if (
            (scene := self.scene()) == Scene.INFRA_DETAILS
        ) and not EnterRoomSolver().detect_room() == self.room:
            EnterRoomSolver().run(self.room)
        elif (
            scene in [Scene.ORDER_LIST, Scene.FACTORY_ROOMS]
            and not self.detect_room_inside() == self.room
        ):
            EnterRoomSolver().run(self.room)

    def transition(self) -> bool:
        if (scene := self.scene()) == Scene.RIIC_OPERATOR_SELECT:
            if self.riic_agent_choose():
                self.sleep(self.check_wait_time())
                self.tap_element("confirm_blue")
                self.success = True
        elif self.find("room_detail"):
            if self.success:
                return True
            else:
                self.tap((1600, 200))
        elif scene in [Scene.ORDER_LIST, Scene.FACTORY_ROOMS]:
            if self.success:
                return True
            else:
                self.tap((500, 970))
        elif scene == Scene.INFRA_ARRANGE_ORDER:
            self.scene_graph_step(Scene.RIIC_OPERATOR_SELECT)
        elif scene == Scene.INFRA_ARRANGE_CONFIRM:
            self.scene_graph_step(Scene.RIIC_OPERATOR_SELECT)
        elif scene in self.waiting_scene:
            self.waiting_solver()
        else:
            EnterRoomSolver().run(self.room)
