# core/terrain_manager.py
import time
import urllib

from panda3d.core import GeoMipTerrain, PNMImage, Texture, Vec3, NodePath
from panda3d.core import Filename, Material, ColorAttrib, AmbientLight, DirectionalLight
import os

from scene import util


class TerrainManager:
    """地形管理类"""

    def __init__(self, world):
        self.world = world
        self.terrains = []

    # core/terrain_manager.py
    def _get_tree_widget(self):
        """安全获取树形控件"""
        try:
            if (hasattr(self.world, 'interface_manager') and
                    hasattr(self.world.interface_manager, 'treeWidget')):
                return self.world.interface_manager.treeWidget
        except AttributeError:
            pass
        return None

    def createTerrainFromHeightMap(self, heightmap_path, scale=(1, 1, 1)):
        """从高度图创建地形"""
        # 检查文件是否存在
        if not os.path.exists(heightmap_path):
            print(f"错误: 高度图文件 {heightmap_path} 不存在")
            return None

        try:
            print(f"🔆 开始创建高度图地形")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_terrains = []
            try:
                parent_item, parent_node = target_parents[0]

                # 创建GeoMipTerrain对象
                terrain_name = f"terrain_{len(self.terrains)}_{int(time.time() * 1000000) % 10000}"
                terrain = GeoMipTerrain(terrain_name)

                # 加载高度图
                height_image = PNMImage(Filename.fromOsSpecific(heightmap_path))

                # 检查并调整图像尺寸为2的幂次方加1
                width, height = height_image.getXSize(), height_image.getYSize()
                print(f"原始图像尺寸: {width}x{height}")

                # 找到最接近的有效尺寸
                valid_sizes = [17, 33, 65, 129, 257, 513, 1025, 2049]
                target_size = 129  # 默认尺寸

                # 选择最接近的尺寸
                max_dim = max(width, height)
                for size in valid_sizes:
                    if size >= max_dim:
                        target_size = size
                        break
                else:
                    target_size = valid_sizes[-1]  # 使用最大尺寸

                # 如果需要，调整图像尺寸
                if width != target_size or height != target_size:
                    print(f"调整图像尺寸从 {width}x{height} 到 {target_size}x{target_size}")
                    # 使用正确的图像缩放方法
                    resized_image = PNMImage(target_size, target_size)
                    resized_image.quickFilterFrom(height_image)
                    height_image = resized_image

                # 使用正确的方法设置高度图
                terrain.setHeightfield(height_image)

                # 设置地形参数
                terrain.setBruteforce(True)  # 使用LOD

                terrain.setBlockSize(32)
                # terrain.setNearFarThreshold(50.0,200.0)

                # 生成地形
                terrain.generate()

                # 获取地形节点
                terrain_node = terrain.getRoot()

                if terrain_node.isEmpty():
                    print("错误：无法生成有效的地形节点")
                    return None

                terrain_node.setTag("is_scene_element", "1")
                terrain_node.setTag("tree_item_type", "TERRAIN_NODE")

                node_name = f"Terrain_{os.path.basename(heightmap_path)}_{len(self.terrains)}"
                terrain_node.setName(node_name)

                center_offset = (target_size - 1) / 2
                terrain_node.setPos(-center_offset * scale[0], -center_offset * scale[1], -5)

                # 设置缩放
                terrain_node.setScale(scale[0], scale[1], scale[2])

                # 将地形添加到场景中
                terrain_node.reparentTo(parent_node)

                from panda3d.core import BitMask32
                # 设置地形节点的碰撞掩码
                terrain_node.setCollideMask(BitMask32.bit(2))  # 使用第2位作为地形碰撞掩码
                # 为地形的所有子节点也设置碰撞掩码
                for child in terrain_node.getChildren():
                    child.setCollideMask(BitMask32.bit(2))

                # 添加材质
                self._applyTerrainMaterial(terrain_node)

                terrain_node.setPythonTag("selectable", True)

                # 保存地形信息（包括高度图的副本）
                terrain_info = {
                    'terrain': terrain,
                    'node': terrain_node,
                    'heightmap': heightmap_path,
                    'heightfield': height_image,  # 保存高度图副本
                    'scale': scale,
                    'name': node_name
                }

                self.terrains.append(terrain_info)

                print(f"✅ 为 {parent_item.text(0)} 创建高度图地形: {terrain_name}")

                # 在Qt树形控件中添加对应节点
                qt_item = tree_widget.add_node_to_tree_widget(terrain_node, parent_item, "TERRAIN_NODE")
                if qt_item:
                    created_terrains.append((terrain_node, qt_item))
                else:
                    created_terrains.append((terrain_node, None))
                    print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

            except Exception as e:
                print(f"❌ 为 {parent_item.text(0)} 创建高度图地形: {str(e)}")
                return None

                # 处理创建结果
            if not created_terrains:
                print("❌ 没有成功创建任何高度图地形")
                return None

                # 选中最后创建的光源
            if created_terrains:
                last_light_np, last_qt_item = created_terrains[-1]
                if last_qt_item:
                    tree_widget.setCurrentItem(last_qt_item)
                    # 更新选择和属性面板
                    tree_widget.update_selection_and_properties(last_light_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_terrains)} 个高度图地形")

            # 返回值处理
            if len(created_terrains) == 1:
                return created_terrains[0][0]  # 单个光源返回NodePath
            else:
                return [light_np for light_np, _ in created_terrains]  # 多个光源返回列表

        except Exception as e:
            print(f"❌ 创建高度图地形过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def createFlatTerrain(self, size=(0.3, 0.3), resolution=129):
        """创建平面地形"""
        try:
            print(f"🔆 开始创建平面地形")

            # 获取树形控件
            tree_widget = self._get_tree_widget()
            if not tree_widget:
                print("❌ 无法访问树形控件")
                return None

            # 获取目标父节点列表
            target_parents = tree_widget.get_target_parents_for_creation()
            if not target_parents:
                print("❌ 没有找到有效的父节点")
                return None

            created_terrains = []

            try:
                parent_item, parent_node = target_parents[0]
                # 确保分辨率是2的幂次方加1 (如129, 257, 513等)
                valid_resolutions = [17, 33, 65, 129, 257, 513, 1025]
                if resolution not in valid_resolutions:
                    # 找到最接近的有效分辨率
                    closest_res = min(valid_resolutions, key=lambda x: abs(x - resolution))
                    print(f"警告: 分辨率 {resolution} 不是有效的地形分辨率，使用 {closest_res}")
                    resolution = closest_res

                # 创建GeoMipTerrain对象
                terrain_name = f"flat_terrain_{len(self.terrains)}_{int(time.time() * 1000000) % 10000}"
                terrain = GeoMipTerrain(terrain_name)

                # 创建一个平面高度图，尺寸必须是2^n+1
                height_image = PNMImage(resolution, resolution)
                height_image.fill(0.5)  # 设置为中等高度 (0.0-1.0范围)

                # 使用正确的方法设置高度图
                terrain.setHeightfield(height_image)
                terrain.setBruteforce(True)  # 设置LOD

                # 设置地形参数
                terrain.setBlockSize(32)  # 设置块大小

                # 生成地形
                terrain.generate()

                # 获取地形节点
                terrain_node = terrain.getRoot()

                if terrain_node.isEmpty():
                    print("错误：无法生成有效的平面地形节点")
                    return None

                terrain_node.setTag("is_scene_element", "1")
                terrain_node.setTag("tree_item_type", "TERRAIN_NODE")

                node_name = f"FlatTerrain_{len(self.terrains)}_{int(time.time() * 1000000) % 10000}"
                terrain_node.setName(node_name)

                center_offset = (resolution - 1) / 2
                terrain_node.setPos(-center_offset * size[0], -center_offset * size[1], 0)

                # 将地形添加到场景中
                terrain_node.reparentTo(parent_node)

                # 为地形添加碰撞体
                from panda3d.core import BitMask32
                # 设置地形节点的碰撞掩码
                terrain_node.setCollideMask(BitMask32.bit(2))  # 使用第2位作为地形碰撞掩码
                # 为地形的所有子节点也设置碰撞掩码
                for child in terrain_node.getChildren():
                    child.setCollideMask(BitMask32.bit(2))

                # 添加材质
                self._applyTerrainMaterial(terrain_node)

                # 保存地形信息（包括高度图）
                terrain_info = {
                    'terrain': terrain,
                    'node': terrain_node,
                    'heightmap': None,
                    'heightfield': height_image,  # 保存高度图
                    'scale': (size[0], size[1], 50),
                    'name': node_name
                }

                self.terrains.append(terrain_info)

                print(f"✅ 为 {parent_item.text(0)} 创建平面地形: {terrain_name}")

                # 在Qt树形控件中添加对应节点
                qt_item = tree_widget.add_node_to_tree_widget(terrain_node, parent_item, "TERRAIN_NODE")
                if qt_item:
                    created_terrains.append((terrain_node, qt_item))
                else:
                    created_terrains.append((terrain_node, None))
                    print("⚠️ Qt树节点添加失败，但Panda3D对象已创建")

            except Exception as e:
                print(f"❌ 为 {parent_item.text(0)} 创建平面地形: {str(e)}")
                return None

                # 处理创建结果
            if not created_terrains:
                print("❌ 没有成功创建任何平面地形")
                return None

                # 选中最后创建的光源
            if created_terrains:
                last_light_np, last_qt_item = created_terrains[-1]
                if last_qt_item:
                    tree_widget.setCurrentItem(last_qt_item)
                    # 更新选择和属性面板
                    tree_widget.update_selection_and_properties(last_light_np, last_qt_item)

            print(f"🎉 总共创建了 {len(created_terrains)} 个平面地形")

            # 返回值处理
            if len(created_terrains) == 1:
                return created_terrains[0][0]  # 单个光源返回NodePath
            else:
                return [light_np for light_np, _ in created_terrains]  # 多个光源返回列表

        except Exception as e:
            print(f"❌ 创建平面地形过程失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    def _applyTerrainMaterial(self, terrain_node):
        """为地形应用材质"""
        try:
            # 创建材质
            material = Material()
            material.setAmbient((0.5, 0.5, 0.5, 1.0))  # 环境光反射
            material.setDiffuse((1, 1, 1, 1.0))  # 漫反射（绿色调）
            material.setSpecular((0.2, 0.2, 0.2, 1.0))  # 镜面反射
            material.setShininess(5.0)  # 高光强度

            # 应用材质到地形
            terrain_node.setMaterial(material)

            # 启用材质
            terrain_node.setAttrib(ColorAttrib.makeDefault())

            print("✓ 地形材质已应用")
        except Exception as e:
            print(f"应用地形材质时出错: {e}")

    def updateTerrain(self):
        """更新所有地形的LOD"""
        for terrain_info in self.terrains:
            terrain = terrain_info['terrain']
            if hasattr(terrain, 'update'):
                terrain.update()

    def setTerrainLODThreshold(self, terrain_info, threshold):
        """设置地形LOD阈值"""
        if terrain_info in self.terrains:
            terrain = terrain_info['terrain']
            # GeoMipTerrain可能没有setNearFarThreshold方法，使用其他方式设置LOD
            print("注意: GeoMipTerrain可能不支持直接设置LOD阈值")

    def getTerrainHeight(self, terrain_info, x, y):
        """获取地形上指定点的高度"""
        if terrain_info in self.terrains:
            terrain = terrain_info['terrain']
            if hasattr(terrain, 'getElevation'):
                return terrain.getElevation(x, y)
        return 0

    # core/terrain_manager.py

    def modifyTerrainHeight(self, terrain_info, x, y, radius, strength, operation="add"):
        """修改地形高度"""
        if terrain_info not in self.terrains:
            return False

        try:
            terrain = terrain_info['terrain']
            terrain_node = terrain_info['node']  # 获取地形节点

            # 直接使用我们保存的高度图数据
            if 'heightfield' not in terrain_info:
                print("地形信息中没有保存高度图数据")
                return False

            heightmap = terrain_info['heightfield']
            if not heightmap:
                print("无法获取地形高度图数据")
                return False

            # 获取高度图尺寸
            width = heightmap.getXSize()
            height = heightmap.getYSize()

            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()

            # 正确的坐标转换方法
            # 1. 将世界坐标转换为相对于地形中心的坐标
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()

            # 2. 考虑地形的缩放
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()

            # 3. 转换为高度图坐标
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)

            print(f"坐标转换详情:")
            print(f"  世界坐标: ({x:.2f}, {y:.2f})")
            print(f"  地形位置: ({terrain_pos.getX():.2f}, {terrain_pos.getY():.2f})")
            print(f"  地形缩放: ({terrain_scale.getX():.2f}, {terrain_scale.getY():.2f})")
            print(f"  相对坐标: ({relative_x:.2f}, {relative_y:.2f})")
            print(f"  缩放坐标: ({scaled_x:.2f}, {scaled_y:.2f})")
            print(f"  中心偏移: {center_offset}")
            print(f"  高度图坐标: ({center_x}, {center_y})")
            print(f"  高度图尺寸: {width} x {height}")

            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                print(f"❌ 中心点坐标超出范围: ({center_x}, {center_y})")
                print(f"❌ 有效范围: (0-{width - 1}, 0-{height - 1})")
                return False

            # 修改半径范围内的点
            modified = False

            # 使用正确的半径计算（考虑地形缩放）
            effective_radius = max(1, int(radius / max(terrain_scale.getX(), terrain_scale.getY())))

            print(f"有效编辑半径: {effective_radius}")

            # 限制编辑半径不超过地形尺寸的1/4，避免性能问题
            effective_radius = min(effective_radius, width // 4, height // 4)

            for dx in range(-effective_radius, effective_radius + 1):
                for dy in range(-effective_radius, effective_radius + 1):
                    # 检查距离
                    distance = (dx * dx + dy * dy) ** 0.5
                    if distance <= effective_radius:
                        # 计算影响权重
                        weight = 1.0 - (distance / effective_radius) if effective_radius > 0 else 1.0

                        # 计算目标点坐标
                        target_x = center_x + dx
                        target_y = center_y + dy

                        # 检查边界
                        if 0 <= target_x < width and 0 <= target_y < height:
                            # 获取当前高度
                            current_height = heightmap.getRed(target_x, target_y)

                            # 根据操作类型修改高度
                            if operation == "add":
                                new_height = min(1.0, current_height + strength * weight * 0.01)
                            elif operation == "subtract":
                                new_height = max(0.0, current_height - strength * weight * 0.01)
                            elif operation == "set":
                                # 平坦化操作，设置为中等高度
                                new_height = 0.5
                            else:
                                new_height = current_height

                            # 设置新高度
                            heightmap.setRed(target_x, target_y, new_height)
                            heightmap.setGreen(target_x, target_y, new_height)
                            heightmap.setBlue(target_x, target_y, new_height)
                            modified = True

            # 如果有修改，则重新生成地形
            if modified:
                # 重新设置高度图并生成地形
                terrain.setHeightfield(heightmap)
                terrain.generate()

                # 关键修复：重新创建碰撞体，因为在生成地形时会丢失原有碰撞体
                from panda3d.core import BitMask32, CollisionNode, CollisionPlane, Plane, Point3
                # 移除旧的地形碰撞体（如果存在）
                for child in terrain_node.getChildren():
                    if isinstance(child.node(), CollisionNode) and child.getName().startswith("terrain_collision_"):
                        child.removeNode()

                # 创建新的地形碰撞体
                # 使用更简单的方法：直接给地形节点添加碰撞体
                terrain_node.setCollideMask(BitMask32.bit(2))  # 确保地形节点本身具有碰撞掩码

                # 为地形的每个子节点也设置碰撞掩码
                for child in terrain_node.getChildren():
                    child.setCollideMask(BitMask32.bit(2))

                print(f"✓ 地形高度已修改: 位置({x}, {y}), 半径{radius}, 操作{operation}")
                print(f"✓ 重新设置了地形碰撞体")

            return modified

        except Exception as e:
            print(f"修改地形高度时出错: {e}")
            import traceback
            traceback.print_exc()
            return False

    def deleteTerrain(self, terrain_info):
        """删除地形"""
        if terrain_info in self.terrains:
            # 从场景中移除地形节点
            terrain_node = terrain_info['node']
            if terrain_node and not terrain_node.isEmpty():
                tree_widget = self._get_tree_widget()
                if tree_widget:
                    tree_widget.delete_items(tree_widget.selectedItems())
            #     terrain_node.removeNode()
            #
            # # 从列表中移除
            # self.terrains.remove(terrain_info)

            # # 更新场景树
            # if hasattr(self.world, 'scene_manager') and hasattr(self.world.scene_manager, 'updateSceneTree'):
            #     self.world.scene_manager.updateSceneTree()

            print(f"✓ 地形已删除: {terrain_info.get('name', 'Unknown')}")

            import gc
            gc.collect()

            return True
        return False

    def getTerrainByName(self, name):
        """根据名称查找地形"""
        for terrain_info in self.terrains:
            if terrain_info.get('name') == name:
                return terrain_info
        return None

    def getAllTerrainNames(self):
        """获取所有地形名称"""
        return [terrain_info.get('name', f'Terrain_{i}') for i, terrain_info in enumerate(self.terrains)]

    def setTerrainColor(self, terrain_info, color):
        """设置地形颜色"""
        try:
            if terrain_info in self.terrains:
                terrain_node = terrain_info['node']
                if terrain_node:
                    # 创建新材质
                    material = Material()
                    material.setAmbient((color[0] * 0.5, color[1] * 0.5, color[2] * 0.5, 1.0))
                    material.setDiffuse((color[0], color[1], color[2], 1.0))
                    material.setSpecular((0.2, 0.2, 0.2, 1.0))
                    material.setShininess(5.0)

                    # 应用材质
                    terrain_node.setMaterial(material)
                    print(f"✓ 地形颜色已更新: {color}")
                    return True
            return False
        except Exception as e:
            print(f"设置地形颜色时出错: {e}")
            return False

    def setTerrainTexture(self, terrain_info, texture_path):
        """为地形设置纹理"""
        try:
            if terrain_info in self.terrains:
                terrain_node = terrain_info['node']
                if terrain_node and os.path.exists(texture_path):
                    texture_path = util.normalize_model_path(texture_path)
                    # 加载纹理
                    texture = self.world.loader.loadTexture(texture_path)
                    if texture:
                        # 应用纹理
                        terrain_node.setTexture(texture, 1)
                        print(f"✓ 地形纹理已应用: {texture_path}")
                        return True
                    else:
                        print(f"✗ 无法加载纹理: {texture_path}")
                else:
                    print(f"✗ 纹理文件不存在: {texture_path}")
            return False
        except Exception as e:
            print(f"应用地形纹理时出错: {e}")
            return False

    def saveTerrainData(self, terrain_info, filename):
        """保存地形数据到文件"""
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']

            # 保存高度图到文件
            if heightfield:
                # 创建保存路径
                terrain_dir = os.path.join(os.path.dirname(filename), "terrains")
                if not os.path.exists(terrain_dir):
                    os.makedirs(terrain_dir)

                # 生成唯一的地形文件名
                terrain_filename = f"terrain_{terrain_info.get('name', 'unnamed')}_{int(time.time())}.png"
                terrain_path = os.path.join(terrain_dir, terrain_filename)

                # 保存高度图
                heightfield.write(Filename.fromOsSpecific(terrain_path))

                # 保存地形信息到标签
                terrain_node.setTag("terrain_heightmap_path", terrain_path)
                terrain_node.setTag("terrain_scale", str(terrain_info['scale']))

                print(f"✓ 地形数据已保存: {terrain_path}")
                return terrain_path
            return None
        except Exception as e:
            print(f"保存地形数据时出错: {e}")
            return None

    def _recreateTerrain(self, terrain_node):
        """重新创建地形"""
        try:
            print(f"重新创建地形: {terrain_node.getName()}")

            # 获取保存的地形信息
            heightmap_path = terrain_node.getTag("terrain_heightmap_path") if terrain_node.hasTag(
                "terrain_heightmap_path") else None
            scale_str = terrain_node.getTag("terrain_scale") if terrain_node.hasTag("terrain_scale") else "(1, 1, 1)"

            # 解析缩放信息
            try:
                scale_str = scale_str.strip("()")
                scale_values = [float(x.strip()) for x in scale_str.split(",")]
                scale = (scale_values[0], scale_values[1], scale_values[2]) if len(scale_values) >= 3 else (1, 1, 1)
            except:
                scale = (1, 1, 1)

            # 恢复位置信息
            if terrain_node.hasTag("transform_pos"):
                try:
                    pos_str = terrain_node.getTag("transform_pos")
                    pos_str = pos_str.replace('LVecBase3f', '').replace('LPoint3f', '').strip('()')
                    pos_values = [float(x.strip()) for x in pos_str.split(',')]
                    if len(pos_values) >= 3:
                        terrain_node.setPos(pos_values[0], pos_values[1], pos_values[2])
                except Exception as e:
                    print(f"恢复地形位置失败: {e}")

            # 如果有高度图路径，重新创建地形
            if heightmap_path and os.path.exists(heightmap_path):
                # 使用现有方法重新创建地形
                new_terrain = self.createTerrainFromHeightMap(heightmap_path, scale)
                if new_terrain:
                    # 删除旧的地形节点
                    if not terrain_node.isEmpty():
                        terrain_node.removeNode()
                    return new_terrain

            # 如果没有高度图或创建失败，创建一个平面地形作为替代
            print("使用平面地形作为替代")
            new_terrain = self.createFlatTerrain(size=(scale[0], scale[1]), resolution=129)
            if new_terrain:
                # 删除旧的地形节点
                if not terrain_node.isEmpty():
                    terrain_node.removeNode()
                return new_terrain

            return terrain_node
        except Exception as e:
            print(f"重新创建地形失败: {e}")
            return terrain_node

