import bmesh
import mathutils
import traceback

from . import pattern
from .pattern import pattern_list
from . import g


class Loop:
    def __init__(self):
        self.b_and_d_list = []  # 存储边界和方向的元组 (boundary, direction)

        self.bvh_tree = None
        self.pattern = None  # create要调用到,没声明报错
        self.mid_co = mathutils.Vector((0, 0, 0))

        self.solver_msg = ""
        self.suggest_solution = []
        self.solver_constraint_list = [
            None for _ in range(8)
        ]  # 丑陋储存法，用None作占位符，现存的pattern最多有八个约束（6个padding 2个x y 附加边流）
        self.solver_constraint_rotation = None
        self.solver_constraint_pattern = None

    def add_edge(self, boundary, direction):
        self.b_and_d_list.append((boundary, direction))
        self.update_mid_co()

    def update_mid_co(self):
        self.mid_co = sum(
            [b.mid_co for b in self.boundaries], mathutils.Vector()
        ) / len(self.boundaries)

    def normalize(self):
        """标准化环路，将每条边的顶点索引进行排序，忽略边的方向"""
        return sorted(
            [
                (id(boundary), boundary)  # Sort by the unique id of the boundary object
                for boundary, _ in self.b_and_d_list
            ]
        )

    def __eq__(self, other):
        """重载 '==' 运算符，用来进行环路set的去重"""
        return self.normalize() == other.normalize()

    def __hash__(self):
        """重载 '__hash__' 使其能够作为字典的键进行去重"""
        return hash(tuple(self.normalize()))

    def __repr__(self):
        return f"Loop({self.b_and_d_list})"

    @property
    def length(self):
        return len(self.b_and_d_list)

    @property
    def shape(self):
        # 这个是指边数
        return [len(b.verts) - 1 for b, _ in self.b_and_d_list]

    @property
    def boundaries(self):
        return [b for b, _ in self.b_and_d_list]

    @property
    def boundary_verts_walk(self):
        # 有重复，用于createmesh的时候walk，即每个boundary的头尾都是重叠的
        verts = []
        for b, d in self.b_and_d_list:
            if d:
                boundary_verts = b.verts
            else:
                boundary_verts = b.verts[::-1]
            for v in boundary_verts:
                verts.append(v)

        return verts

    @property
    def verts(self):
        # 无重复顶点
        verts = []
        seen = set()  # 用来跟踪已访问的顶点

        for b, d in self.b_and_d_list:
            if d:
                boundary_verts = b.verts  # 如果方向为真，按顺序获取顶点
            else:
                boundary_verts = b.verts[::-1]  # 如果方向为假，反转顶点顺序

            # 遍历当前边界的顶点
            for v in boundary_verts:
                if v not in seen:  # 如果顶点未被访问过
                    verts.append(v)
                    seen.add(v)  # 标记该顶点为已访问

        return verts

    @property
    def controll_verts(self):
        # 即start和end的列表，无重复顶点
        controll_verts = []

        for idx, (b, d) in enumerate(self.b_and_d_list):
            if d:
                vert_1 = b.start_vertex
                vert_2 = b.end_vertex
            else:
                vert_1 = b.end_vertex
                vert_2 = b.start_vertex

            controll_verts.append(vert_1)

        return controll_verts

    @property
    def edges(self):
        edges = []
        for b, d in self.b_and_d_list:
            if d:
                boundary_edges = b.edges
            else:
                boundary_edges = b.edges[::-1]

            for e in boundary_edges:
                edges.append(e)

        return edges

    # @property
    # def controll_verts(self):
    #     # 控制点，即每个boundary的start end
    #     return set(v for b in self.boundaries for v in b.verts)

    @property
    def current_solution(self):
        if self.pattern and self.pattern.solution:
            return self.pattern.solution

        return None

    def create_bvh_tree(self):
        temp_bm = bmesh.new()

        temp_bm_verts = []
        for b in self.boundaries:
            for vert in b.verts:
                vert = temp_bm.verts.new(vert.co)
                temp_bm_verts.append(vert)

        # 确保新创建的顶点的索引是正确的
        temp_bm.verts.index_update()
        temp_bm.verts.ensure_lookup_table()

        temp_bm.faces.new(temp_bm_verts)  # ngon

        bvh_tree = mathutils.bvhtree.BVHTree.FromBMesh(temp_bm)
        self.bvh_tree = bvh_tree

        temp_bm.free()

    def recreate_pattern(self):
        # 尝试创建pattern

        self.remove_pattern()
        try:
            pattern_instance = pattern.recreate_pattern(self)
        except Exception as e:
            pattern_instance = None
            error_info = traceback.format_exc()
            g.running_exception = e

        bmesh.update_edit_mesh(g.obj_me)

        self.pattern = pattern_instance

        g.is_redraw_pattern = True
        return self.pattern

    def remove_pattern(self):
        if self.pattern:
            self.pattern.remove_mesh()
            self.pattern = None

    def get_pattern_constraint(self):
        # 获取当前pattern，如果有约束则返回约束，无约束则返回当前pattern实例，如无解则返回None
        if self.solver_constraint_pattern:
            return self.solver_constraint_pattern
        elif self.pattern:
            # 不应返回pattern实例，后面还要copy，有些属性无法复制
            for i in pattern.pattern_list:
                if i.name == self.pattern.name:
                    return i
        return None

    def change_solver_constraint_rotation(self):
        current_pattern = self.get_pattern_constraint()
        if not current_pattern:
            return

        rotation = self.solver_constraint_rotation
        if rotation is None:
            if self.pattern:
                new_rotation = self.pattern.rotation + 1
            else:
                new_rotation = 0
        else:
            new_rotation = rotation + 1
        new_rotation = new_rotation % len(self.boundaries)

        self.solver_constraint_rotation = new_rotation
        self.solver_constraint_pattern = current_pattern
        self.recreate_pattern()

    def next_solver_constraint_pattern(self, is_auto_search=True):
        current_pattern = self.get_pattern_constraint()

        # 初始化约束，设置pattern约束
        boundary_num = len(self.boundaries)
        pattern_list_current = [
            i for i in pattern_list if boundary_num == i.boundary_num
        ]
        self.init_solver_constraint()
        if current_pattern is None:
            loop_start_idx = 0
        else:
            loop_start_idx = next(
                i
                for i, obj in enumerate(pattern_list_current)
                if obj.name == current_pattern.name
            )

        for pattern in (
            pattern_list_current[loop_start_idx + 1 :]
            + pattern_list_current[:loop_start_idx]
        ):  # 不跳过第一个，会导致无法切换到下一个
            self.solver_constraint_pattern = pattern
            result = self.recreate_pattern()
            if not is_auto_search:
                # 不是自动模式，则拿到第一个就溜
                return

            if result is not None:
                return
        # 找不到，回到原处
        self.solver_constraint_pattern = current_pattern
        self.recreate_pattern()

    def init_solver_constraint(
        self, is_init_list=True, is_init_rotation=True, is_init_pattern=True
    ):
        # 另外两个约束不能单独保留
        if is_init_pattern:
            is_init_list = True
            is_init_rotation = True

        if is_init_list:
            self.solver_constraint_list = [None for _ in range(len(self.shape) + 2)]
        if is_init_rotation:
            self.solver_constraint_rotation = None

        if is_init_pattern:
            self.solver_constraint_pattern = None

        self.recreate_pattern()

    def change_solver_constraint_list(self, key, change):
        current_pattern = self.get_pattern_constraint()

        if not current_pattern:
            return

        if not self.solver_constraint_pattern and not self.pattern:
            return

        if isinstance(key, int):
            solution_idx = key
        else:
            edge_var_count = len(current_pattern.additional_edge_flow_info)
            if key == "x" and edge_var_count >= 1:
                solution_idx = len(self.shape) - 1 + 1
            elif key == "y" and edge_var_count >= 2:
                solution_idx = len(self.shape) - 1 + 2
            elif key == "z" and edge_var_count >= 3:
                solution_idx = len(self.shape) - 1 + 3
            else:
                return

        # 是否有约束，没有再找solution
        solver_constraint_list = self.solver_constraint_list
        old_value = solver_constraint_list[solution_idx]

        if old_value is None:
            current_solution = self.current_solution
            if current_solution:
                old_value = current_solution[solution_idx]
        if old_value is None:
            old_value = 0

        new_value = old_value + change
        if new_value < 0:
            return

        upper = max(self.shape)
        if new_value > upper:
            return

        self.solver_constraint_list[solution_idx] = new_value
        self.solver_constraint_pattern = current_pattern
        self.recreate_pattern()

    def get_boundary_index(self, boundary_input):
        for idx, b in enumerate(self.boundaries):
            if b == boundary_input:
                return idx
