"""
地形编辑工具模块
"""

import math
import random
from panda3d.core import PNMImage

class TerrainTools:
    """
    地形编辑工具类
    提供完整的地形编辑功能，包括抬高、降低、平坦化、平滑、噪声等工具
    """
    
    def __init__(self, world):
        self.world = world
        self.current_tool = "抬高工具"
        self.radius = 10.0
        self.strength = 50.0
        self.operation = "添加"
        self.is_editing = False
        self.last_edit_pos = None
        self.brush_spacing = 1.0  # 画笔间隔，用于连续编辑
        
        # 工具参数
        self.smooth_factor = 0.5
        self.noise_scale = 1.0
        self.erosion_factor = 0.1
        self.thermal_erosion_factor = 0.05
        
    def set_tool(self, tool_name):
        """
        设置当前工具
        """
        valid_tools = ["抬高工具", "降低工具", "平坦化工具", "平滑工具", "噪声工具", "侵蚀工具", "热侵蚀工具"]
        if tool_name in valid_tools:
            self.current_tool = tool_name
            print(f"设置地形编辑工具: {tool_name}")
            return True
        else:
            print(f"无效的工具名称: {tool_name}")
            return False
        
    def set_radius(self, radius):
        """
        设置工具半径
        """
        self.radius = max(0.1, min(100.0, float(radius)))
        
    def set_strength(self, strength):
        """
        设置工具强度
        """
        self.strength = max(0.0, min(100.0, float(strength)))
        
    def set_operation(self, operation):
        """
        设置操作类型
        """
        valid_operations = ["添加", "减去", "设置"]
        if operation in valid_operations:
            self.operation = operation
        else:
            print(f"无效的操作类型: {operation}")
            
    def set_smooth_factor(self, factor):
        """
        设置平滑因子
        """
        self.smooth_factor = max(0.0, min(1.0, float(factor)))
        
    def set_noise_scale(self, scale):
        """
        设置噪声缩放
        """
        self.noise_scale = max(0.1, min(10.0, float(scale)))
        
    def set_erosion_factor(self, factor):
        """
        设置侵蚀因子
        """
        self.erosion_factor = max(0.0, min(1.0, float(factor)))
        
    def set_thermal_erosion_factor(self, factor):
        """
        设置热侵蚀因子
        """
        self.thermal_erosion_factor = max(0.0, min(1.0, float(factor)))
        
    def set_brush_spacing(self, spacing):
        """
        设置画笔间隔
        """
        self.brush_spacing = max(0.1, min(10.0, float(spacing)))
        
    def start_editing(self, x, y):
        """
        开始编辑地形
        """
        self.is_editing = True
        self.last_edit_pos = (x, y)
        self._apply_tool(x, y)
        
    def update_editing(self, x, y):
        """
        更新编辑过程
        """
        if self.is_editing:
            # 检查是否需要应用工具（基于画笔间隔）
            if self.last_edit_pos:
                distance = math.sqrt((x - self.last_edit_pos[0])**2 + (y - self.last_edit_pos[1])**2)
                if distance >= self.brush_spacing:
                    self._apply_tool(x, y)
                    self.last_edit_pos = (x, y)
            else:
                self._apply_tool(x, y)
                self.last_edit_pos = (x, y)
            
    def stop_editing(self):
        """
        停止编辑地形
        """
        self.is_editing = False
        self.last_edit_pos = None
        
    def _apply_tool(self, x, y):
        """
        应用当前工具到指定位置
        """
        # 获取当前选中的地形
        selected_terrain = self._get_selected_terrain()
        if not selected_terrain:
            # 如果没有选中地形，尝试获取场景中的第一个地形
            selected_terrain = self._get_first_terrain()
            if not selected_terrain:
                print("没有可用的地形")
                return
            
        # 根据当前工具类型应用不同的操作
        if self.current_tool == "抬高工具":
            self._raise_terrain(selected_terrain, x, y)
        elif self.current_tool == "降低工具":
            self._lower_terrain(selected_terrain, x, y)
        elif self.current_tool == "平坦化工具":
            self._flatten_terrain(selected_terrain, x, y)
        elif self.current_tool == "平滑工具":
            self._smooth_terrain(selected_terrain, x, y)
        elif self.current_tool == "噪声工具":
            self._noise_terrain(selected_terrain, x, y)
        elif self.current_tool == "侵蚀工具":
            self._erode_terrain(selected_terrain, x, y)
        elif self.current_tool == "热侵蚀工具":
            self._thermal_erode_terrain(selected_terrain, x, y)
            
    def _get_selected_terrain(self):
        """
        获取当前选中的地形
        """
        # 如果有选择系统，获取选中的地形
        if hasattr(self.world, 'selection') and self.world.selection.selectedNode:
            selected_node = self.world.selection.selectedNode
            # 检查选中的节点是否是地形节点
            if selected_node.getTag("tree_item_type") == "TERRAIN_NODE":
                # 在地形管理器中查找对应的地形信息
                if hasattr(self.world, 'terrain_manager'):
                    for terrain_info in self.world.terrain_manager.terrains:
                        if terrain_info['node'] == selected_node:
                            return terrain_info
        return None
        
    def _get_first_terrain(self):
        """
        获取场景中的第一个地形
        """
        if hasattr(self.world, 'terrain_manager') and self.world.terrain_manager.terrains:
            return self.world.terrain_manager.terrains[0]
        return None
        
    def _raise_terrain(self, terrain_info, x, y):
        """
        抬高地形
        """
        if hasattr(self.world, 'terrain_manager'):
            operation = "add"
            if self.operation == "减去":
                operation = "subtract"
            elif self.operation == "设置":
                operation = "set"
                
            self.world.terrain_manager.modifyTerrainHeight(
                terrain_info, x, y, self.radius, self.strength, operation
            )
            
    def _lower_terrain(self, terrain_info, x, y):
        """
        降低地形
        """
        if hasattr(self.world, 'terrain_manager'):
            operation = "subtract"
            if self.operation == "添加":
                operation = "add"
            elif self.operation == "设置":
                operation = "set"
                
            self.world.terrain_manager.modifyTerrainHeight(
                terrain_info, x, y, self.radius, self.strength, operation
            )
            
    def _flatten_terrain(self, terrain_info, x, y):
        """
        平坦化地形
        """
        if hasattr(self.world, 'terrain_manager'):
            # 平坦化工具使用特殊的"set"操作
            self.world.terrain_manager.modifyTerrainHeight(
                terrain_info, x, y, self.radius, self.strength, "set"
            )
            
    def _smooth_terrain(self, terrain_info, x, y):
        """
        平滑地形
        """
        if not hasattr(self.world, 'terrain_manager'):
            return
            
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()
            
            # 坐标转换
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)
            
            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                return
                
            # 计算有效编辑半径
            effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
            effective_radius = min(effective_radius, width // 4, height // 4)
            
            # 创建临时高度图用于存储平滑结果
            temp_heightfield = PNMImage(width, height)
            temp_heightfield.copyFrom(heightfield)
            
            # 应用平滑算法
            modified = False
            for dx in range(-effective_radius, effective_radius + 1):
                for dy in range(-effective_radius, effective_radius + 1):
                    distance = math.sqrt(dx*dx + dy*dy)
                    if distance <= effective_radius:
                        target_x = center_x + dx
                        target_y = center_y + dy
                        
                        # 检查边界
                        if 0 <= target_x < width and 0 <= target_y < height:
                            # 计算邻域平均值
                            total_height = 0.0
                            count = 0
                            
                            # 检查周围的8个邻居
                            for nx in range(-1, 2):
                                for ny in range(-1, 2):
                                    neighbor_x = target_x + nx
                                    neighbor_y = target_y + ny
                                    
                                    # 检查邻居是否在边界内
                                    if 0 <= neighbor_x < width and 0 <= neighbor_y < height:
                                        total_height += heightfield.getRed(neighbor_x, neighbor_y)
                                        count += 1
                                        
                            if count > 0:
                                average_height = total_height / count
                                current_height = heightfield.getRed(target_x, target_y)
                                
                                # 应用平滑因子
                                new_height = current_height * (1 - self.smooth_factor) + average_height * self.smooth_factor
                                
                                # 设置新高度到临时高度图
                                temp_heightfield.setRed(target_x, target_y, new_height)
                                temp_heightfield.setGreen(target_x, target_y, new_height)
                                temp_heightfield.setBlue(target_x, target_y, new_height)
                                modified = True
                                
            # 如果有修改，则更新地形
            if modified:
                # 更新高度图
                terrain_info['heightfield'] = temp_heightfield
                
                # 重新设置高度图并生成地形
                terrain = terrain_info['terrain']
                terrain.setHeightfield(temp_heightfield)
                terrain.generate()
                
                # 重新创建碰撞体
                self._recreate_collision(terrain_node)
                
                print(f"✓ 地形已平滑: 位置({x}, {y}), 半径{self.radius}")
                
        except Exception as e:
            print(f"平滑地形时出错: {e}")
            
    def _noise_terrain(self, terrain_info, x, y):
        """
        添加噪声到地形
        """
        if not hasattr(self.world, 'terrain_manager'):
            return
            
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()
            
            # 坐标转换
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)
            
            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                return
                
            # 计算有效编辑半径
            effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
            effective_radius = min(effective_radius, width // 4, height // 4)
            
            # 应用噪声算法
            modified = False
            for dx in range(-effective_radius, effective_radius + 1):
                for dy in range(-effective_radius, effective_radius + 1):
                    distance = math.sqrt(dx*dx + dy*dy)
                    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:
                            # 生成噪声值
                            noise_value = random.uniform(-1.0, 1.0) * self.noise_scale
                            
                            # 获取当前高度
                            current_height = heightfield.getRed(target_x, target_y)
                            
                            # 应用噪声
                            new_height = max(0.0, min(1.0, current_height + noise_value * weight * self.strength * 0.01))
                            
                            # 设置新高度
                            heightfield.setRed(target_x, target_y, new_height)
                            heightfield.setGreen(target_x, target_y, new_height)
                            heightfield.setBlue(target_x, target_y, new_height)
                            modified = True
                            
            # 如果有修改，则更新地形
            if modified:
                # 重新设置高度图并生成地形
                terrain = terrain_info['terrain']
                terrain.setHeightfield(heightfield)
                terrain.generate()
                
                # 重新创建碰撞体
                self._recreate_collision(terrain_node)
                
                print(f"✓ 地形已添加噪声: 位置({x}, {y}), 半径{self.radius}")
                
        except Exception as e:
            print(f"添加噪声到地形时出错: {e}")
            
    def _erode_terrain(self, terrain_info, x, y):
        """
        水力侵蚀地形
        """
        if not hasattr(self.world, 'terrain_manager'):
            return
            
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()
            
            # 坐标转换
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)
            
            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                return
                
            # 计算有效编辑半径
            effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
            effective_radius = min(effective_radius, width // 4, height // 4)
            
            # 应用简单的侵蚀算法
            modified = False
            for dx in range(-effective_radius, effective_radius + 1):
                for dy in range(-effective_radius, effective_radius + 1):
                    distance = math.sqrt(dx*dx + dy*dy)
                    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 = heightfield.getRed(target_x, target_y)
                            
                            # 根据高度确定侵蚀效果
                            # 高度越高，侵蚀越强
                            erosion_amount = self.erosion_factor * weight * (1.0 - current_height) * self.strength * 0.01
                            
                            # 应用侵蚀
                            new_height = max(0.0, min(1.0, current_height - erosion_amount))
                            
                            # 设置新高度
                            heightfield.setRed(target_x, target_y, new_height)
                            heightfield.setGreen(target_x, target_y, new_height)
                            heightfield.setBlue(target_x, target_y, new_height)
                            modified = True
                            
            # 如果有修改，则更新地形
            if modified:
                # 重新设置高度图并生成地形
                terrain = terrain_info['terrain']
                terrain.setHeightfield(heightfield)
                terrain.generate()
                
                # 重新创建碰撞体
                self._recreate_collision(terrain_node)
                
                print(f"✓ 地形已应用侵蚀: 位置({x}, {y}), 半径{self.radius}")
                
        except Exception as e:
            print(f"侵蚀地形时出错: {e}")
            
    def _thermal_erode_terrain(self, terrain_info, x, y):
        """
        热侵蚀地形（模拟重力作用）
        """
        if not hasattr(self.world, 'terrain_manager'):
            return
            
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()
            
            # 坐标转换
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)
            
            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                return
                
            # 计算有效编辑半径
            effective_radius = max(1, int(self.radius / max(terrain_scale.getX(), terrain_scale.getY())))
            effective_radius = min(effective_radius, width // 4, height // 4)
            
            # 应用热侵蚀算法
            modified = False
            for dx in range(-effective_radius, effective_radius + 1):
                for dy in range(-effective_radius, effective_radius + 1):
                    distance = math.sqrt(dx*dx + dy*dy)
                    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 = heightfield.getRed(target_x, target_y)
                            lowest_neighbor_height = current_height
                            lowest_neighbor_x = target_x
                            lowest_neighbor_y = target_y
                            
                            # 检查8个邻居
                            for nx in range(-1, 2):
                                for ny in range(-1, 2):
                                    if nx == 0 and ny == 0:
                                        continue  # 跳过中心点
                                        
                                    neighbor_x = target_x + nx
                                    neighbor_y = target_y + ny
                                    
                                    # 检查邻居是否在边界内
                                    if 0 <= neighbor_x < width and 0 <= neighbor_y < height:
                                        neighbor_height = heightfield.getRed(neighbor_x, neighbor_y)
                                        if neighbor_height < lowest_neighbor_height:
                                            lowest_neighbor_height = neighbor_height
                                            lowest_neighbor_x = neighbor_x
                                            lowest_neighbor_y = neighbor_y
                                            
                            # 如果有更低的邻居，移动部分物质
                            height_diff = current_height - lowest_neighbor_height
                            if height_diff > 0:
                                # 移动物质
                                move_amount = min(height_diff, self.thermal_erosion_factor * weight * self.strength * 0.01)
                                
                                # 降低当前点
                                new_height = max(0.0, current_height - move_amount)
                                heightfield.setRed(target_x, target_y, new_height)
                                heightfield.setGreen(target_x, target_y, new_height)
                                heightfield.setBlue(target_x, target_y, new_height)
                                
                                # 提高最低邻居点
                                neighbor_new_height = min(1.0, lowest_neighbor_height + move_amount * 0.5)
                                heightfield.setRed(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
                                heightfield.setGreen(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
                                heightfield.setBlue(lowest_neighbor_x, lowest_neighbor_y, neighbor_new_height)
                                
                                modified = True
                                
            # 如果有修改，则更新地形
            if modified:
                # 重新设置高度图并生成地形
                terrain = terrain_info['terrain']
                terrain.setHeightfield(heightfield)
                terrain.generate()
                
                # 重新创建碰撞体
                self._recreate_collision(terrain_node)
                
                print(f"✓ 地形已应用热侵蚀: 位置({x}, {y}), 半径{self.radius}")
                
        except Exception as e:
            print(f"热侵蚀地形时出错: {e}")
            
    def _recreate_collision(self, terrain_node):
        """
        重新创建地形碰撞体
        """
        try:
            from panda3d.core import BitMask32
            
            # 移除旧的地形碰撞体
            for child in terrain_node.getChildren():
                if child.getName().startswith("terrain_collision_"):
                    child.removeNode()
                    
            # 设置地形节点的碰撞掩码
            terrain_node.setCollideMask(BitMask32.bit(2))
            
            # 为地形的每个子节点也设置碰撞掩码
            for child in terrain_node.getChildren():
                child.setCollideMask(BitMask32.bit(2))
                
        except Exception as e:
            print(f"重新创建碰撞体时出错: {e}")
            
    def apply_brush(self, terrain_info, x, y, brush_data):
        """
        应用画笔数据到地形
        """
        if not hasattr(self.world, 'terrain_manager'):
            return
            
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 获取地形节点信息
            terrain_pos = terrain_node.getPos()
            terrain_scale = terrain_node.getScale()
            
            # 坐标转换
            relative_x = x - terrain_pos.getX()
            relative_y = y - terrain_pos.getY()
            scaled_x = relative_x / terrain_scale.getX()
            scaled_y = relative_y / terrain_scale.getY()
            center_offset = (width - 1) / 2
            center_x = int(scaled_x + center_offset)
            center_y = int(scaled_y + center_offset)
            
            # 检查中心点是否在有效范围内
            if not (0 <= center_x < width and 0 <= center_y < height):
                return
                
            # 应用画笔数据
            modified = False
            brush_size = len(brush_data)
            brush_radius = brush_size // 2
            
            for dx in range(-brush_radius, brush_radius + 1):
                for dy in range(-brush_radius, brush_radius + 1):
                    if 0 <= brush_radius + dx < brush_size and 0 <= brush_radius + dy < brush_size:
                        brush_value = brush_data[brush_radius + dy][brush_radius + dx]
                        
                        target_x = center_x + dx
                        target_y = center_y + dy
                        
                        # 检查边界
                        if 0 <= target_x < width and 0 <= target_y < height:
                            # 获取当前高度
                            current_height = heightfield.getRed(target_x, target_y)
                            
                            # 应用画笔值
                            new_height = max(0.0, min(1.0, current_height + brush_value * self.strength * 0.01))
                            
                            # 设置新高度
                            heightfield.setRed(target_x, target_y, new_height)
                            heightfield.setGreen(target_x, target_y, new_height)
                            heightfield.setBlue(target_x, target_y, new_height)
                            modified = True
                            
            # 如果有修改，则更新地形
            if modified:
                # 重新设置高度图并生成地形
                terrain = terrain_info['terrain']
                terrain.setHeightfield(heightfield)
                terrain.generate()
                
                # 重新创建碰撞体
                self._recreate_collision(terrain_node)
                
                print(f"✓ 应用画笔数据到地形: 位置({x}, {y})")
                
        except Exception as e:
            print(f"应用画笔数据到地形时出错: {e}")