using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Utils.CoordinateSystem;

namespace NavisworksTransport.PathPlanning
{
    /// <summary>
    /// 坡度分析器
    /// 负责分析通道的坡度特征和高度变化规律
    /// </summary>
    public class SlopeAnalyzer
    {
        private readonly Dictionary<string, ChannelSlopeInfo> _slopeCache;
        private readonly double _minSlopeAngle; // 最小坡度角度（弧度）
        private readonly ICoordinateSystem _coordinateSystem;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="minSlopeAngle">最小坡度角度（度），小于此角度认为是平面</param>
        public SlopeAnalyzer(double minSlopeAngle = 1.0)
        {
            _slopeCache = new Dictionary<string, ChannelSlopeInfo>();
            _minSlopeAngle = minSlopeAngle * Math.PI / 180.0; // 转换为弧度
            _coordinateSystem = CoordinateSystemManager.Instance.Current;
        }

        /// <summary>
        /// 分析通道类型和坡度特征
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <returns>通道坡度信息</returns>
        public ChannelSlopeInfo AnalyzeChannelSlope(ModelItem channel)
        {
            try
            {
                if (channel?.Geometry?.BoundingBox == null)
                {
                    LogManager.Warning("[坡度分析] 通道没有有效的几何信息");
                    return CreateDefaultSlopeInfo();
                }

                LogManager.Debug($"[坡度分析] 开始分析通道坡度: {channel.DisplayName}");

                // 检查缓存
                var cacheKey = channel.InstanceGuid.ToString();
                if (_slopeCache.TryGetValue(cacheKey, out var cachedInfo))
                {
                    LogManager.Debug($"[坡度分析] 使用缓存的坡度信息: 角度={cachedInfo.SlopeAngle * 180 / Math.PI:F2}度, 类型={cachedInfo.Type}");
                    return cachedInfo;
                }

                // 分析通道几何特征
                var slopeInfo = AnalyzeGeometrySlope(channel);
                
                // 缓存结果
                _slopeCache[cacheKey] = slopeInfo;

                LogManager.Info($"[坡度分析] 通道坡度分析完成: 角度={slopeInfo.SlopeAngle * 180 / Math.PI:F2}度, 类型={slopeInfo.Type}, 方向=({slopeInfo.SlopeDirection.X:F2}, {slopeInfo.SlopeDirection.Y:F2}, {slopeInfo.SlopeDirection.Z:F2})");
                return slopeInfo;
            }
            catch (Exception ex)
            {
                LogManager.Error($"[坡度分析] 分析通道坡度时发生错误: {ex.Message}");
                return CreateDefaultSlopeInfo();
            }
        }

        /// <summary>
        /// 根据坡度计算精确Z坐标
        /// </summary>
        /// <param name="position">目标位置</param>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="baseHeight">基准高度</param>
        /// <returns>精确的Z坐标</returns>
        public double CalculateAccurateZCoordinate(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
        {
            try
            {
                LogManager.Debug($"[坡度分析] 计算位置 ({position.X:F2}, {position.Y:F2}) 的精确Z坐标");

                // 对于平面通道，直接返回基准高度
                if (slopeInfo.Type == SlopeType.Flat || Math.Abs(slopeInfo.SlopeAngle) < _minSlopeAngle)
                {
                    return baseHeight;
                }

                // 根据坡度类型计算Z坐标
                switch (slopeInfo.Type)
                {
                    case SlopeType.Ramp:
                        return CalculateRampHeight(position, slopeInfo, baseHeight);
                    
                    case SlopeType.Stairs:
                        return CalculateStairsHeight(position, slopeInfo, baseHeight);
                    
                    case SlopeType.CurvedRamp:
                        return CalculateCurvedRampHeight(position, slopeInfo, baseHeight);
                    
                    default:
                        return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
                }
            }
            catch (Exception ex)
            {
                LogManager.Warning($"[坡度分析] 计算精确Z坐标时发生错误: {ex.Message}，使用基准高度");
                return baseHeight;
            }
        }

        /// <summary>
        /// 分析几何体的坡度特征
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <returns>坡度信息</returns>
        private ChannelSlopeInfo AnalyzeGeometrySlope(ModelItem channel)
        {
            var bounds = channel.Geometry.BoundingBox;
            var slopeInfo = new ChannelSlopeInfo
            {
                Type = DetermineSlopeType(channel),
                SlopePoints = new List<Point3D>()
            };

            // 添加边界点作为坡度采样点
            AddBoundaryPoints(slopeInfo, bounds);

            // 计算坡度角度和方向
            CalculateSlopeAngleAndDirection(slopeInfo, bounds);

            // 根据通道类型进行特殊处理
            SpecializedSlopeAnalysis(channel, slopeInfo);

            return slopeInfo;
        }

        /// <summary>
        /// 确定坡度类型
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <returns>坡度类型</returns>
        private SlopeType DetermineSlopeType(ModelItem channel)
        {
            var displayName = channel.DisplayName?.ToLower() ?? "";
            var className = channel.ClassDisplayName?.ToLower() ?? "";

            if (displayName.Contains("楼梯") || displayName.Contains("stair") || 
                className.Contains("楼梯") || className.Contains("stair"))
            {
                return SlopeType.Stairs;
            }
            
            if (displayName.Contains("坡道") || displayName.Contains("ramp") || 
                className.Contains("坡道") || className.Contains("ramp"))
            {
                return SlopeType.Ramp;
            }

            // 根据几何特征判断
            var bounds = channel.Geometry.BoundingBox;
            var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
            var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
            var heightDiff = maxElevation - minElevation;
            var horizontalLength = Math.Max(maxH1 - minH1, maxH2 - minH2);
            
            if (heightDiff > 0.1 && horizontalLength > 0.1) // 有明显高度差
            {
                var slopeRatio = heightDiff / horizontalLength;
                
                if (slopeRatio > 0.5) // 坡度大于50%，可能是楼梯
                {
                    return SlopeType.Stairs;
                }
                else if (slopeRatio > 0.05) // 坡度大于5%，可能是坡道
                {
                    return SlopeType.Ramp;
                }
            }

            return SlopeType.Flat; // 默认为平面
        }

        /// <summary>
        /// 添加边界点
        /// </summary>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="bounds">边界框</param>
        private void AddBoundaryPoints(ChannelSlopeInfo slopeInfo, BoundingBox3D bounds)
        {
            // 添加8个边界框顶点
            var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
            var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, minH2, minElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, minH2, minElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, maxH2, minElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, maxH2, minElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, minH2, maxElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, minH2, maxElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(minH1, maxH2, maxElevation));
            slopeInfo.SlopePoints.Add(_coordinateSystem.CreatePoint(maxH1, maxH2, maxElevation));

            // 添加中心线上的采样点
            int sampleCount = 5;
            for (int i = 0; i < sampleCount; i++)
            {
                double ratio = (double)i / (sampleCount - 1);
                var samplePoint = _coordinateSystem.CreatePoint(
                    minH1 + (maxH1 - minH1) * ratio,
                    minH2 + (maxH2 - minH2) * ratio,
                    minElevation + (maxElevation - minElevation) * ratio);
                slopeInfo.SlopePoints.Add(samplePoint);
            }
        }

        /// <summary>
        /// 计算坡度角度和方向
        /// </summary>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="bounds">边界框</param>
        private void CalculateSlopeAngleAndDirection(ChannelSlopeInfo slopeInfo, BoundingBox3D bounds)
        {
            // 计算主要方向向量
            var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
            var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
            var directionH1 = maxH1 - minH1;
            var directionH2 = maxH2 - minH2;
            var directionElevation = maxElevation - minElevation;

            // 选择主要的水平方向
            var horizontalLength = Math.Max(Math.Abs(directionH1), Math.Abs(directionH2));
            
            if (horizontalLength > 0.001)
            {
                // 计算坡度角度
                slopeInfo.SlopeAngle = Math.Atan(Math.Abs(directionElevation) / horizontalLength);
                
                // 计算坡度方向向量
                var directionPoint = _coordinateSystem.CreatePoint(directionH1, directionH2, directionElevation);
                var length = Math.Sqrt(directionPoint.X * directionPoint.X + directionPoint.Y * directionPoint.Y + directionPoint.Z * directionPoint.Z);
                if (length > 0.001)
                {
                    slopeInfo.SlopeDirection = new Vector3D(
                        directionPoint.X / length,
                        directionPoint.Y / length,
                        directionPoint.Z / length
                    );
                }
                else
                {
                    slopeInfo.SlopeDirection = new Vector3D(0, 0, 1); // 默认向上
                }
            }
            else
            {
                slopeInfo.SlopeAngle = 0;
                var up = _coordinateSystem.UpVector;
                slopeInfo.SlopeDirection = new Vector3D(up.X, up.Y, up.Z);
            }
        }

        /// <summary>
        /// 特殊坡度分析
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <param name="slopeInfo">坡度信息</param>
        private void SpecializedSlopeAnalysis(ModelItem channel, ChannelSlopeInfo slopeInfo)
        {
            switch (slopeInfo.Type)
            {
                case SlopeType.Stairs:
                    AnalyzeStairsDetails(channel, slopeInfo);
                    break;
                
                case SlopeType.Ramp:
                    AnalyzeRampDetails(channel, slopeInfo);
                    break;
            }
        }

        /// <summary>
        /// 分析楼梯详细信息
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <param name="slopeInfo">坡度信息</param>
        private void AnalyzeStairsDetails(ModelItem channel, ChannelSlopeInfo slopeInfo)
        {
            try
            {
                var bounds = channel.Geometry.BoundingBox;
                var (minElevation, maxElevation) = _coordinateSystem.GetHeightRange(bounds);
                var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
                var totalHeight = maxElevation - minElevation;
                var totalLength = Math.Max(maxH1 - minH1, maxH2 - minH2);

                // 估算楼梯级数（假设每级高度15-20cm）
                var estimatedSteps = (int)(totalHeight / 0.175); // 平均17.5cm每级
                if (estimatedSteps < 1) estimatedSteps = 1;

                slopeInfo.StepCount = estimatedSteps;
                slopeInfo.StepHeight = totalHeight / estimatedSteps;
                slopeInfo.StepDepth = totalLength / estimatedSteps;

                LogManager.Debug($"[坡度分析] 楼梯详细信息: 级数={estimatedSteps}, 每级高度={slopeInfo.StepHeight:F3}m, 每级深度={slopeInfo.StepDepth:F3}m");
            }
            catch (Exception ex)
            {
                LogManager.Warning($"[坡度分析] 分析楼梯详细信息时发生错误: {ex.Message}");
            }
        }

        /// <summary>
        /// 分析坡道详细信息
        /// </summary>
        /// <param name="channel">通道模型项</param>
        /// <param name="slopeInfo">坡度信息</param>
        private void AnalyzeRampDetails(ModelItem channel, ChannelSlopeInfo slopeInfo)
        {
            try
            {
                var bounds = channel.Geometry.BoundingBox;
                
                // 检查是否为弯曲坡道（通过长宽比判断）
                var (minH1, maxH1, minH2, maxH2) = _coordinateSystem.GetHorizontalRange(bounds);
                var width = Math.Min(maxH1 - minH1, maxH2 - minH2);
                var length = Math.Max(maxH1 - minH1, maxH2 - minH2);
                
                if (width > 0 && length / width > 3.0) // 长宽比大于3:1，可能是弯曲坡道
                {
                    slopeInfo.Type = SlopeType.CurvedRamp;
                    LogManager.Debug($"[坡度分析] 检测到弯曲坡道: 长度={length:F2}m, 宽度={width:F2}m");
                }
            }
            catch (Exception ex)
            {
                LogManager.Warning($"[坡度分析] 分析坡道详细信息时发生错误: {ex.Message}");
            }
        }

        /// <summary>
        /// 计算坡道高度
        /// </summary>
        /// <param name="position">位置点</param>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="baseHeight">基准高度</param>
        /// <returns>计算得到的高度</returns>
        private double CalculateRampHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
        {
            return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
        }

        /// <summary>
        /// 计算楼梯高度
        /// </summary>
        /// <param name="position">位置点</param>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="baseHeight">基准高度</param>
        /// <returns>计算得到的高度</returns>
        private double CalculateStairsHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
        {
            if (slopeInfo.StepCount <= 0 || slopeInfo.StepHeight <= 0)
            {
                return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
            }

            // 找到楼梯的起点和终点
            var startPoint = slopeInfo.SlopePoints.OrderBy(p => _coordinateSystem.GetElevation(p)).First();
            var endPoint = slopeInfo.SlopePoints.OrderBy(p => _coordinateSystem.GetElevation(p)).Last();

            // 计算位置在楼梯长度方向上的投影
            var (startH1, startH2) = _coordinateSystem.GetHorizontalCoords(startPoint);
            var (endH1, endH2) = _coordinateSystem.GetHorizontalCoords(endPoint);
            var (positionH1, positionH2) = _coordinateSystem.GetHorizontalCoords(position);
            var totalDistance = Math.Sqrt(
                Math.Pow(endH1 - startH1, 2) +
                Math.Pow(endH2 - startH2, 2)
            );

            var currentDistance = Math.Sqrt(
                Math.Pow(positionH1 - startH1, 2) +
                Math.Pow(positionH2 - startH2, 2)
            );

            if (totalDistance <= 0.001)
            {
                return baseHeight;
            }

            // 计算当前位置对应的楼梯级数
            var ratio = currentDistance / totalDistance;
            var currentStep = (int)(ratio * slopeInfo.StepCount);
            
            // 楼梯的高度是阶梯状的
            return baseHeight + currentStep * slopeInfo.StepHeight;
        }

        /// <summary>
        /// 计算弯曲坡道高度
        /// </summary>
        /// <param name="position">位置点</param>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="baseHeight">基准高度</param>
        /// <returns>计算得到的高度</returns>
        private double CalculateCurvedRampHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
        {
            // 弯曲坡道使用样条插值，这里先简化为线性插值
            return CalculateLinearSlopeHeight(position, slopeInfo, baseHeight);
        }

        /// <summary>
        /// 计算线性坡度高度
        /// </summary>
        /// <param name="position">位置点</param>
        /// <param name="slopeInfo">坡度信息</param>
        /// <param name="baseHeight">基准高度</param>
        /// <returns>计算得到的高度</returns>
        private double CalculateLinearSlopeHeight(Point3D position, ChannelSlopeInfo slopeInfo, double baseHeight)
        {
            if (Math.Abs(slopeInfo.SlopeAngle) < _minSlopeAngle)
            {
                return baseHeight;
            }

            // 计算沿坡度方向的距离
            var projectedDistance = position.X * slopeInfo.SlopeDirection.X + 
                                  position.Y * slopeInfo.SlopeDirection.Y;

            // 根据坡度角度计算高度增量
            var heightIncrement = projectedDistance * Math.Tan(slopeInfo.SlopeAngle);
            
            return baseHeight + heightIncrement;
        }

        /// <summary>
        /// 创建默认坡度信息
        /// </summary>
        /// <returns>默认坡度信息</returns>
        private ChannelSlopeInfo CreateDefaultSlopeInfo()
        {
            return new ChannelSlopeInfo
            {
                Type = SlopeType.Flat,
                SlopeAngle = 0,
                SlopeDirection = new Vector3D(0, 0, 1),
                SlopePoints = new List<Point3D>()
            };
        }

        /// <summary>
        /// 清除坡度缓存
        /// </summary>
        public void ClearCache()
        {
            _slopeCache.Clear();
            LogManager.Info("[坡度分析] 坡度缓存已清除");
        }

        /// <summary>
        /// 获取缓存统计信息
        /// </summary>
        /// <returns>缓存项数量</returns>
        public int GetCacheCount()
        {
            return _slopeCache.Count;
        }
    }

    /// <summary>
    /// 通道坡度信息
    /// </summary>
    public class ChannelSlopeInfo
    {
        /// <summary>
        /// 坡度角度（弧度）
        /// </summary>
        public double SlopeAngle { get; set; }

        /// <summary>
        /// 坡度类型
        /// </summary>
        public SlopeType Type { get; set; }

        /// <summary>
        /// 坡度方向向量（单位向量）
        /// </summary>
        public Vector3D SlopeDirection { get; set; }

        /// <summary>
        /// 坡度特征点集合
        /// </summary>
        public List<Point3D> SlopePoints { get; set; } = new List<Point3D>();

        /// <summary>
        /// 楼梯级数（仅对楼梯有效）
        /// </summary>
        public int StepCount { get; set; }

        /// <summary>
        /// 每级楼梯高度（仅对楼梯有效）
        /// </summary>
        public double StepHeight { get; set; }

        /// <summary>
        /// 每级楼梯深度（仅对楼梯有效）
        /// </summary>
        public double StepDepth { get; set; }
    }

    /// <summary>
    /// 坡度类型枚举
    /// </summary>
    public enum SlopeType
    {
        /// <summary>
        /// 平面
        /// </summary>
        Flat,

        /// <summary>
        /// 直线坡道
        /// </summary>
        Ramp,

        /// <summary>
        /// 楼梯
        /// </summary>
        Stairs,

        /// <summary>
        /// 弯曲坡道
        /// </summary>
        CurvedRamp,

        /// <summary>
        /// 其他类型
        /// </summary>
        Other
    }

    }
