"""
高级地形编辑工具
提供噪声生成、侵蚀模拟、傅里叶地形等高级功能
"""

import math
import random
import numpy as np
from panda3d.core import PNMImage

class AdvancedTerrainTools:
    """
    高级地形编辑工具类
    提供噪声生成、侵蚀模拟、傅里叶地形等高级功能
    """
    
    def __init__(self, world):
        self.world = world
        
    def generate_perlin_noise(self, terrain_info, scale=1.0, octaves=4, persistence=0.5, lacunarity=2.0):
        """
        生成Perlin噪声地形
        """
        try:
            terrain_node = terrain_info['node']
            heightfield = terrain_info['heightfield']
            
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            # 获取高度图尺寸
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 生成Perlin噪声
            for y in range(height):
                for x in range(width):
                    # 计算噪声坐标
                    nx = x / width * scale
                    ny = y / height * scale
                    
                    # 生成分形噪声
                    noise_value = self._fractal_noise(nx, ny, octaves, persistence, lacunarity)
                    
                    # 将噪声值映射到0-1范围
                    normalized_value = (noise_value + 1) / 2
                    heightfield.setRed(x, y, normalized_value)
                    heightfield.setGreen(x, y, normalized_value)
                    heightfield.setBlue(x, y, normalized_value)
                    
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"生成Perlin噪声时出错: {e}")
            return False
            
    def _fractal_noise(self, x, y, octaves, persistence, lacunarity):
        """
        生成分形噪声
        """
        total = 0.0
        frequency = 1.0
        amplitude = 1.0
        maxValue = 0.0  # 用于归一化
        
        for i in range(octaves):
            total += self._perlin_noise(x * frequency, y * frequency) * amplitude
            maxValue += amplitude
            amplitude *= persistence
            frequency *= lacunarity
            
        return total / maxValue
        
    def _perlin_noise(self, x, y):
        """
        生成Perlin噪声
        """
        # 获取整数部分
        X = int(x) & 255
        Y = int(y) & 255
        
        # 获取小数部分
        x -= int(x)
        y -= int(y)
        
        # 计算平滑曲线
        u = self._fade(x)
        v = self._fade(y)
        
        # 计算梯度
        a = self._gradient(X, Y, x, y)
        b = self._gradient(X + 1, Y, x - 1, y)
        c = self._gradient(X, Y + 1, x, y - 1)
        d = self._gradient(X + 1, Y + 1, x - 1, y - 1)
        
        # 插值
        return self._lerp(v, self._lerp(u, a, b), self._lerp(u, c, d))
        
    def _fade(self, t):
        """
        平滑曲线函数
        """
        return t * t * t * (t * (t * 6 - 15) + 10)
        
    def _lerp(self, t, a, b):
        """
        线性插值
        """
        return a + t * (b - a)
        
    def _gradient(self, hash_val, x, y):
        """
        梯度函数
        """
        h = hash_val & 15
        u = x if h < 8 else y
        v = y if h < 4 else (x if h in [12, 14] else 0)
        return (u if (h & 1) == 0 else -u) + (v if (h & 2) == 0 else -v)
        
    def simulate_hydraulic_erosion(self, terrain_info, iterations=100, rainfall=0.1, solubility=0.01, evaporation=0.01):
        """
        模拟水力侵蚀
        """
        try:
            heightfield = terrain_info['heightfield']
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 初始化水流和沉积物数组
            water = np.zeros((width, height))
            sediment = np.zeros((width, height))
            
            # 执行侵蚀迭代
            for iteration in range(iterations):
                # 添加降雨
                water += rainfall
                
                # 计算水流
                for y in range(height):
                    for x in range(width):
                        current_height = heightfield.getRed(x, y) + water[x, y]
                        
                        # 查找最低邻居
                        lowest_height = current_height
                        lowest_x, lowest_y = x, y
                        
                        for dx in [-1, 0, 1]:
                            for dy in [-1, 0, 1]:
                                if dx == 0 and dy == 0:
                                    continue
                                    
                                nx, ny = x + dx, y + dy
                                if 0 <= nx < width and 0 <= ny < height:
                                    neighbor_height = heightfield.getRed(nx, ny) + water[nx, ny]
                                    if neighbor_height < lowest_height:
                                        lowest_height = neighbor_height
                                        lowest_x, lowest_y = nx, ny
                                        
                        # 移动水流
                        if lowest_x != x or lowest_y != y:
                            flow_amount = min(water[x, y], (current_height - lowest_height) * 0.5)
                            water[x, y] -= flow_amount
                            water[lowest_x, lowest_y] += flow_amount
                            
                            # 溶解物质
                            dissolve_amount = flow_amount * solubility
                            sediment[x, y] -= dissolve_amount
                            sediment[lowest_x, lowest_y] += dissolve_amount
                            
                # 沉积和蒸发
                for y in range(height):
                    for x in range(width):
                        # 蒸发
                        evaporation_amount = min(water[x, y], evaporation)
                        water[x, y] -= evaporation_amount
                        
                        # 沉积
                        if sediment[x, y] > 0:
                            deposit_amount = min(sediment[x, y], 0.01)
                            sediment[x, y] -= deposit_amount
                            new_height = heightfield.getRed(x, y) + deposit_amount
                            heightfield.setRed(x, y, new_height)
                            heightfield.setGreen(x, y, new_height)
                            heightfield.setBlue(x, y, new_height)
                            
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"模拟水力侵蚀时出错: {e}")
            return False
            
    def generate_voronoi_terrain(self, terrain_info, points=100, feature_size=10.0):
        """
        生成Voronoi图地形
        """
        try:
            heightfield = terrain_info['heightfield']
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            width = heightfield.getXSize()
            height = heightfield.getYSize()
            
            # 生成随机点
            voronoi_points = []
            for i in range(points):
                x = random.randint(0, width - 1)
                y = random.randint(0, height - 1)
                height_value = random.random()
                voronoi_points.append((x, y, height_value))
                
            # 为每个像素分配最近点的高度
            for y in range(height):
                for x in range(width):
                    min_distance = float('inf')
                    closest_height = 0.0
                    
                    for px, py, ph in voronoi_points:
                        distance = math.sqrt((x - px)**2 + (y - py)**2)
                        if distance < min_distance:
                            min_distance = distance
                            closest_height = ph
                            
                    # 应用特征大小
                    normalized_distance = min(1.0, min_distance / feature_size)
                    final_height = closest_height * (1.0 - normalized_distance)
                    
                    heightfield.setRed(x, y, final_height)
                    heightfield.setGreen(x, y, final_height)
                    heightfield.setBlue(x, y, final_height)
                    
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"生成Voronoi地形时出错: {e}")
            return False
            
    def generate_fourier_terrain(self, terrain_info, frequency_count=10):
        """
        生成傅里叶地形
        """
        try:
            heightfield = terrain_info['heightfield']
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            width = heightfield.getXSize()
            height_value = heightfield.getYSize()
            
            # 初始化高度图
            for y in range(height_value):
                for x in range(width):
                    heightfield.setRed(x, y, 0.5)
                    heightfield.setGreen(x, y, 0.5)
                    heightfield.setBlue(x, y, 0.5)
                    
            # 添加不同频率的波
            for i in range(frequency_count):
                frequency = 2 ** i
                amplitude = 1.0 / frequency
                
                for y in range(height_value):
                    for x in range(width):
                        # 添加正弦波
                        wave_x = math.sin(2 * math.pi * x * frequency / width) * amplitude
                        wave_y = math.sin(2 * math.pi * y * frequency / height_value) * amplitude
                        current_height = heightfield.getRed(x, y)
                        new_height = current_height + (wave_x + wave_y) * 0.1
                        new_height = max(0.0, min(1.0, new_height))
                        
                        heightfield.setRed(x, y, new_height)
                        heightfield.setGreen(x, y, new_height)
                        heightfield.setBlue(x, y, new_height)
                        
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"生成傅里叶地形时出错: {e}")
            return False
            
    def generate_fault_formation(self, terrain_info, iterations=50, displacement=0.1):
        """
        生成断层地形
        """
        try:
            heightfield = terrain_info['heightfield']
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            width = heightfield.getXSize()
            height_value = heightfield.getYSize()
            
            # 执行断层迭代
            for i in range(iterations):
                # 随机生成断层线
                angle = random.random() * 2 * math.pi
                center_x = random.randint(0, width - 1)
                center_y = random.randint(0, height_value - 1)
                
                # 计算断层方向向量
                dx = math.cos(angle)
                dy = math.sin(angle)
                
                # 移动断层一侧的点
                for y in range(height_value):
                    for x in range(width):
                        # 计算点到断层线的距离和方向
                        distance = (x - center_x) * dx + (y - center_y) * dy
                        
                        if distance > 0:
                            # 提升这一侧的高度
                            current_height = heightfield.getRed(x, y)
                            new_height = current_height + displacement * (iterations - i) / iterations
                            new_height = max(0.0, min(1.0, new_height))
                            
                            heightfield.setRed(x, y, new_height)
                            heightfield.setGreen(x, y, new_height)
                            heightfield.setBlue(x, y, new_height)
                            
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"生成断层地形时出错: {e}")
            return False
            
    def generate_thermal_erosion(self, terrain_info, iterations=100, talus_angle=0.1):
        """
        生成热侵蚀地形
        """
        try:
            heightfield = terrain_info['heightfield']
            if not heightfield:
                print("无法获取地形高度图数据")
                return False
                
            width = heightfield.getXSize()
            height_value = heightfield.getYSize()
            
            # 执行热侵蚀迭代
            for iteration in range(iterations):
                # 创建高度变化数组
                height_changes = np.zeros((width, height_value))
                
                # 计算每个点的侵蚀
                for y in range(height_value):
                    for x in range(width):
                        current_height = heightfield.getRed(x, y)
                        
                        # 检查周围点
                        for dx in [-1, 0, 1]:
                            for dy in [-1, 0, 1]:
                                if dx == 0 and dy == 0:
                                    continue
                                    
                                nx, ny = x + dx, y + dy
                                if 0 <= nx < width and 0 <= ny < height_value:
                                    neighbor_height = heightfield.getRed(nx, ny)
                                    height_diff = current_height - neighbor_height
                                    
                                    # 如果高度差超过临界角度，发生滑坡
                                    if height_diff > talus_angle:
                                        # 移动部分物质到邻居
                                        move_amount = (height_diff - talus_angle) * 0.1
                                        height_changes[x, y] -= move_amount
                                        height_changes[nx, ny] += move_amount * 0.5  # 部分沉积
                                        
                # 应用高度变化
                for y in range(height_value):
                    for x in range(width):
                        if height_changes[x, y] != 0:
                            current_height = heightfield.getRed(x, y)
                            new_height = current_height + height_changes[x, y]
                            new_height = max(0.0, min(1.0, new_height))
                            
                            heightfield.setRed(x, y, new_height)
                            heightfield.setGreen(x, y, new_height)
                            heightfield.setBlue(x, y, new_height)
                            
            # 更新地形
            return self._update_terrain(terrain_info)
            
        except Exception as e:
            print(f"生成热侵蚀地形时出错: {e}")
            return False
            
    def _update_terrain(self, terrain_info):
        """
        更新地形
        """
        try:
            terrain = terrain_info['terrain']
            terrain.setHeightfield(terrain_info['heightfield'])
            terrain.generate()
            
            # 重新创建碰撞体
            self._recreate_collision(terrain_info['node'])
            
            return True
        except Exception as e:
            print(f"更新地形时出错: {e}")
            return False
            
    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}")