using System;
using System.Collections.Generic;
using Autodesk.Navisworks.Api;
using NavisworksTransport.Core.Config;

namespace NavisworksTransport
{
    /// <summary>
    /// 坐标转换工具类
    /// 负责2D地图坐标与3D世界坐标之间的转换
    /// </summary>
    public class CoordinateConverter
    {
        private ChannelBounds _channelBounds;
        private double _mapWidth;
        private double _mapHeight;
        private double _defaultElevation;
        private double _zoomFactor;
        private double _offsetX;
        private double _offsetY;

        /// <summary>
        /// 获取地图边距比例（从配置读取）
        /// </summary>
        private double MarginRatio
        {
            get
            {
                try
                {
                    return ConfigManager.Instance.Current.Visualization.MarginRatio;
                }
                catch
                {
                    return 0.1; // 配置加载失败时使用默认值10%
                }
            }
        }

        /// <summary>
        /// 地图宽度（像素）
        /// </summary>
        public double MapWidth
        {
            get { return _mapWidth; }
            set { _mapWidth = value; }
        }

        /// <summary>
        /// 地图高度（像素）
        /// </summary>
        public double MapHeight
        {
            get { return _mapHeight; }
            set { _mapHeight = value; }
        }

        /// <summary>
        /// 通道边界信息
        /// </summary>
        public ChannelBounds ChannelBounds
        {
            get { return _channelBounds; }
            set { _channelBounds = value; }
        }

        /// <summary>
        /// 默认高程（Z坐标）
        /// </summary>
        public double DefaultElevation
        {
            get { return _defaultElevation; }
            set { _defaultElevation = value; }
        }

        /// <summary>
        /// 当前缩放因子
        /// </summary>
        public double ZoomFactor
        {
            get { return _zoomFactor; }
            set { _zoomFactor = Math.Max(0.1, Math.Min(50.0, value)); } // 限制缩放范围
        }

        /// <summary>
        /// X轴偏移量
        /// </summary>
        public double OffsetX
        {
            get { return _offsetX; }
            set { _offsetX = value; }
        }

        /// <summary>
        /// Y轴偏移量
        /// </summary>
        public double OffsetY
        {
            get { return _offsetY; }
            set { _offsetY = value; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="channelBounds">通道边界信息</param>
        /// <param name="mapWidth">地图窗口宽度</param>
        /// <param name="mapHeight">地图窗口高度</param>
        public CoordinateConverter(ChannelBounds channelBounds, double mapWidth, double mapHeight)
        {
            _channelBounds = channelBounds ?? throw new ArgumentNullException(nameof(channelBounds));
            _mapWidth = mapWidth;
            _mapHeight = mapHeight;
            
            // 默认使用通道底部高程
            _defaultElevation = channelBounds.MinPoint.Z + 0.1; // 稍微抬高一点避免在地面上
            
            // 自动计算最佳视图
            CalculateBestFitView();
        }

        public CoordinateConverter()
        {
        }

        /// <summary>
        /// 计算最佳适应视图
        /// </summary>
        public void CalculateBestFitView()
        {
            if (_channelBounds == null) return;

            // 计算世界坐标的实际尺寸
            double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
            double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;

            // 如果世界尺寸太小，设置最小显示尺寸
            double minWorldSize = 1.0; // 最小1米
            if (worldWidth < minWorldSize) worldWidth = minWorldSize;
            if (worldHeight < minWorldSize) worldHeight = minWorldSize;

            // 重置缩放和偏移（现在使用简化的坐标转换）
            _zoomFactor = 1.0;
            _offsetX = 0.0;
            _offsetY = 0.0;
        }

        /// <summary>
        /// 将2D地图坐标转换为3D世界坐标
        /// </summary>
        /// <param name="mapPoint">2D地图坐标（像素坐标）</param>
        /// <returns>3D世界坐标</returns>
        public Point3D MapToWorld(MapPoint2D mapPoint)
        {
            if (mapPoint == null)
                throw new ArgumentNullException(nameof(mapPoint));

            // 计算可用的地图尺寸（减去边距）
            double marginRatio = MarginRatio;
            double availableWidth = _mapWidth * (1.0 - 2 * marginRatio);
            double availableHeight = _mapHeight * (1.0 - 2 * marginRatio);

            // 从地图坐标计算相对位置
            double relativeX = (mapPoint.X - _mapWidth * marginRatio) / availableWidth;
            double relativeY = 1.0 - ((mapPoint.Y - _mapHeight * marginRatio) / availableHeight); // Y轴翻转

            // 转换为世界坐标
            double worldX = _channelBounds.MinPoint.X + relativeX * (_channelBounds.MaxPoint.X - _channelBounds.MinPoint.X);
            double worldY = _channelBounds.MinPoint.Y + relativeY * (_channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y);
            double worldZ = _defaultElevation;

            return new Point3D(worldX, worldY, worldZ);
        }

        /// <summary>
        /// 将3D世界坐标转换为2D地图坐标
        /// </summary>
        /// <param name="worldPoint">3D世界坐标</param>
        /// <returns>2D地图坐标（像素坐标）</returns>
        public MapPoint2D WorldToMap(Point3D worldPoint)
        {
            if (worldPoint == null)
                throw new ArgumentNullException(nameof(worldPoint));

            // 计算世界坐标的范围
            double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
            double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;

            // 计算在通道范围内的相对位置（0-1之间）
            double relativeX = (worldPoint.X - _channelBounds.MinPoint.X) / worldWidth;
            double relativeY = (worldPoint.Y - _channelBounds.MinPoint.Y) / worldHeight;

            // 计算可用的地图尺寸（减去边距）
            double marginRatio = MarginRatio;
            double availableWidth = _mapWidth * (1.0 - 2 * marginRatio);
            double availableHeight = _mapHeight * (1.0 - 2 * marginRatio);

            // 转换为地图像素坐标
            double mapX = relativeX * availableWidth + _mapWidth * marginRatio;
            double mapY = (1.0 - relativeY) * availableHeight + _mapHeight * marginRatio; // Y轴翻转

            // 确保坐标在地图范围内
            mapX = Math.Max(0, Math.Min(_mapWidth, mapX));
            mapY = Math.Max(0, Math.Min(_mapHeight, mapY));

            return new MapPoint2D(mapX, mapY);
        }

        /// <summary>
        /// 批量转换2D地图坐标为3D世界坐标
        /// </summary>
        /// <param name="mapPoints">2D地图坐标集合</param>
        /// <returns>3D世界坐标集合</returns>
        public List<Point3D> MapToWorldBatch(IEnumerable<MapPoint2D> mapPoints)
        {
            if (mapPoints == null)
                throw new ArgumentNullException(nameof(mapPoints));

            var worldPoints = new List<Point3D>();
            foreach (var mapPoint in mapPoints)
            {
                worldPoints.Add(MapToWorld(mapPoint));
            }
            return worldPoints;
        }

        /// <summary>
        /// 批量转换3D世界坐标为2D地图坐标
        /// </summary>
        /// <param name="worldPoints">3D世界坐标集合</param>
        /// <returns>2D地图坐标集合</returns>
        public List<MapPoint2D> WorldToMapBatch(IEnumerable<Point3D> worldPoints)
        {
            if (worldPoints == null)
                throw new ArgumentNullException(nameof(worldPoints));

            var mapPoints = new List<MapPoint2D>();
            foreach (var worldPoint in worldPoints)
            {
                mapPoints.Add(WorldToMap(worldPoint));
            }
            return mapPoints;
        }

        /// <summary>
        /// 检查2D地图坐标是否在有效范围内
        /// </summary>
        /// <param name="mapPoint">2D地图坐标</param>
        /// <returns>是否在有效范围内</returns>
        public bool IsValidMapPoint(MapPoint2D mapPoint)
        {
            if (mapPoint == null) return false;
            
            return mapPoint.X >= 0 && mapPoint.X <= _mapWidth &&
                   mapPoint.Y >= 0 && mapPoint.Y <= _mapHeight;
        }

        /// <summary>
        /// 检查3D世界坐标是否在通道范围内
        /// </summary>
        /// <param name="worldPoint">3D世界坐标</param>
        /// <returns>是否在通道范围内</returns>
        public bool IsValidWorldPoint(Point3D worldPoint)
        {
            if (worldPoint == null) return false;
            
            return worldPoint.X >= _channelBounds.MinPoint.X && worldPoint.X <= _channelBounds.MaxPoint.X &&
                   worldPoint.Y >= _channelBounds.MinPoint.Y && worldPoint.Y <= _channelBounds.MaxPoint.Y;
        }

        /// <summary>
        /// 计算地图缩放比例（世界单位/像素）
        /// </summary>
        /// <param name="scaleX">X轴缩放比例</param>
        /// <param name="scaleY">Y轴缩放比例</param>
        public void GetMapScale(out double scaleX, out double scaleY)
        {
            double worldWidth = _channelBounds.MaxPoint.X - _channelBounds.MinPoint.X;
            double worldHeight = _channelBounds.MaxPoint.Y - _channelBounds.MinPoint.Y;
            
            scaleX = worldWidth / _mapWidth;
            scaleY = worldHeight / _mapHeight;
        }

        /// <summary>
        /// 获取地图中心点的世界坐标
        /// </summary>
        /// <returns>地图中心点的3D世界坐标</returns>
        public Point3D GetMapCenterInWorld()
        {
            var mapCenter = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
            return MapToWorld(mapCenter);
        }

        /// <summary>
        /// 根据通道高程调整3D坐标的Z值
        /// </summary>
        /// <param name="worldPoint">3D世界坐标</param>
        /// <param name="elevationOffset">高程偏移量（相对于通道底部）</param>
        /// <returns>调整后的3D坐标</returns>
        public Point3D AdjustElevation(Point3D worldPoint, double elevationOffset = 0.0)
        {
            if (worldPoint == null)
                throw new ArgumentNullException(nameof(worldPoint));

            double adjustedZ = _channelBounds.MinPoint.Z + elevationOffset;
            return new Point3D(worldPoint.X, worldPoint.Y, adjustedZ);
        }

        /// <summary>
        /// 计算两个地图点之间的像素距离
        /// </summary>
        /// <param name="point1">第一个地图点</param>
        /// <param name="point2">第二个地图点</param>
        /// <returns>像素距离</returns>
        public double CalculateMapDistance(MapPoint2D point1, MapPoint2D point2)
        {
            if (point1 == null || point2 == null)
                throw new ArgumentNullException("地图点不能为空");

            double dx = point2.X - point1.X;
            double dy = point2.Y - point1.Y;
            return Math.Sqrt(dx * dx + dy * dy);
        }

        /// <summary>
        /// 计算两个世界坐标点之间的距离
        /// </summary>
        /// <param name="point1">第一个世界坐标点</param>
        /// <param name="point2">第二个世界坐标点</param>
        /// <returns>世界距离（米）</returns>
        public double CalculateWorldDistance(Point3D point1, Point3D point2)
        {
            if (point1 == null || point2 == null)
                throw new ArgumentNullException("世界坐标点不能为空");

            return GeometryHelper.CalculatePointDistance(point1, point2);
        }

        /// <summary>
        /// 更新地图尺寸
        /// </summary>
        /// <param name="newWidth">新的地图宽度</param>
        /// <param name="newHeight">新的地图高度</param>
        public void UpdateMapSize(double newWidth, double newHeight)
        {
            _mapWidth = newWidth;
            _mapHeight = newHeight;
        }

        /// <summary>
        /// 更新通道边界
        /// </summary>
        /// <param name="newChannelBounds">新的通道边界</param>
        public void UpdateChannelBounds(ChannelBounds newChannelBounds)
        {
            _channelBounds = newChannelBounds ?? throw new ArgumentNullException(nameof(newChannelBounds));
            
            // 更新默认高程
            _defaultElevation = _channelBounds.MinPoint.Z + 0.1;
        }

        /// <summary>
        /// 获取转换器信息摘要
        /// </summary>
        /// <returns>转换器信息字符串</returns>
        public override string ToString()
        {
            double scaleX, scaleY;
            GetMapScale(out scaleX, out scaleY);
            return $"坐标转换器 - 地图尺寸:{_mapWidth}x{_mapHeight}, " +
                   $"世界范围:({_channelBounds.MinPoint.X:F2},{_channelBounds.MinPoint.Y:F2}) - " +
                   $"({_channelBounds.MaxPoint.X:F2},{_channelBounds.MaxPoint.Y:F2}), " +
                   $"缩放比例:{scaleX:F4},{scaleY:F4}";
        }

        /// <summary>
        /// 放大视图
        /// </summary>
        /// <param name="factor">放大倍数</param>
        /// <param name="centerPoint">放大中心点（地图坐标）</param>
        public void ZoomIn(double factor = 1.5, MapPoint2D centerPoint = null)
        {
            if (centerPoint == null)
            {
                centerPoint = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
            }

            // 保存当前中心点在世界坐标中的位置
            var worldCenter = MapToWorld(centerPoint);

            // 应用缩放
            _zoomFactor *= factor;
            _zoomFactor = Math.Min(50.0, _zoomFactor); // 限制最大缩放

            // 重新计算偏移以保持中心点位置
            var newMapCenter = WorldToMap(worldCenter);
            _offsetX += centerPoint.X - newMapCenter.X;
            _offsetY += centerPoint.Y - newMapCenter.Y;
        }

        /// <summary>
        /// 缩小视图
        /// </summary>
        /// <param name="factor">缩小倍数</param>
        /// <param name="centerPoint">缩小中心点（地图坐标）</param>
        public void ZoomOut(double factor = 1.5, MapPoint2D centerPoint = null)
        {
            if (centerPoint == null)
            {
                centerPoint = new MapPoint2D(_mapWidth / 2.0, _mapHeight / 2.0);
            }

            // 保存当前中心点在世界坐标中的位置
            var worldCenter = MapToWorld(centerPoint);

            // 应用缩放
            _zoomFactor /= factor;
            _zoomFactor = Math.Max(0.1, _zoomFactor); // 限制最小缩放

            // 重新计算偏移以保持中心点位置
            var newMapCenter = WorldToMap(worldCenter);
            _offsetX += centerPoint.X - newMapCenter.X;
            _offsetY += centerPoint.Y - newMapCenter.Y;
        }

        /// <summary>
        /// 重置视图到最佳适应
        /// </summary>
        public void ResetView()
        {
            CalculateBestFitView();
        }

        /// <summary>
        /// 平移视图
        /// </summary>
        /// <param name="deltaX">X轴移动距离（像素）</param>
        /// <param name="deltaY">Y轴移动距离（像素）</param>
        public void Pan(double deltaX, double deltaY)
        {
            _offsetX += deltaX;
            _offsetY += deltaY;
        }
    }
} 