/* ==============================================================================
 * 功能描述：SpaceExtension  
 * 创 建 者：Garrett
 * 创建日期：2019/6/26 17:40:25
 * ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Mechanical;
using JBIM.Definition;
using RevitToJBim.Common;
using SAGA.RevitUtils;
using SAGA.RevitUtils.Extends;
using XYZ = Autodesk.Revit.DB.XYZ;

namespace RevitToJBim.Extension
{
    /// <summary>
    /// SpaceExtension
    /// </summary>
    public static class SpaceExtension
    {
        public readonly static string UseablePhaseName = "阶段1";

        /// <summary>
        /// 获取系统使用的阶段Id
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public static ElementId GetUsePhaseId(this Document doc)
        {
            var phase = GetUsePhase(doc);
            if (phase == null)
            {
                return ElementId.InvalidElementId;
            }
            return phase.Id;
        }
        /// <summary>
        /// 获取系统使用的阶段
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public static Phase GetUsePhase(this Document doc)
        {
            var elements = doc.GetElements<Phase>(BuiltInCategory.OST_Phases);
            foreach (var element in elements)
            {
                var tempName = element.Name.Replace(" ", "").Trim();
                if (UseablePhaseName == tempName)
                {
                    return element;
                }
            }
            return null;
        }

        public static ElementId GetCurrentPhaseId(this Space space)
        {
            return space.GetParameterElementId(BuiltInParameter.ROOM_PHASE_ID) ?? ElementId.InvalidElementId;
        }
        /// <summary>
        /// 判断是否是阶段1的空间
        /// </summary>
        /// <param name="space"></param>
        /// <returns></returns>
        public static bool IsPhase1Space(this Space space)
        {
            var doc = space.Document;
            var useId = GetUsePhaseId(doc);
            return space.GetCurrentPhaseId() == useId;
        }
        /// <summary>
        /// 空间标高是否是当前使用视图标高
        /// </summary>
        /// <param name="space"></param>
        /// <returns></returns>
        public static bool IsViewLevel(this Space space)
        {
            var doc = space.Document;
            var useViewId = doc.GetUseView();
            if (useViewId == null)
            {
                return false;
            }
            return space.Level?.Id == useViewId.GenLevel?.Id;
        }


        /// <summary>
        /// 获取空间的外轮廓
        /// </summary>
        /// <param name="space"></param>
        /// <returns></returns>
        public static List<Polygon> GetSpaceOutline(this Space space)
        {
            List<Polygon> outlines=new List<Polygon>();
            try
            {
                SpatialElementBoundaryOptions options = new SpatialElementBoundaryOptions();
                //options.SpatialElementBoundaryLocation = SpatialElementBoundaryLocation.Finish;
                var segments = space.GetBoundarySegments(options);
                double tolerance = space.Document.Application.ShortCurveTolerance;
                foreach (var segment in segments)
                {
                    #region 取外轮廓的点

                    List<XYZ> xyzs = new List<XYZ>();
                    foreach (BoundarySegment boundarySegment in segment)
                    {
                        var segmentElement = space.Document.GetElement(boundarySegment.ElementId);
                        if (segmentElement is Wall wall)
                        {
                            var wallCurve = wall.GetLocationCurve();
                            var parallelWalls = GetParallelWalls(wall);
                            var segmentCurve = boundarySegment.GetCurve();
                            var tessellates = segmentCurve.Tessellate();
                            foreach (XYZ tessellate in tessellates)
                            {
                                var project = wallCurve.GetProjectPt(tessellate);
                                var verticalVector = (project - tessellate).Normalize();
                                var tempXyz = GetOuterXYZ(tessellate, verticalVector, segmentCurve, parallelWalls);
                                if (tempXyz != null)
                                    xyzs.Add(tempXyz);
                            }
                        }
                        else
                        {
                            foreach (XYZ tessellate in boundarySegment.GetCurve().Tessellate())
                            {
                                xyzs.Add(tessellate);
                            }
                        }
                    }

                    #endregion

                    #region 处理内拐角

                    int count = xyzs.Count;
                    /*
                     * 相邻两条线求交点
                     * 处理内拐角
                     */
                    for (int i = 0; i < count; i++)
                    {
                        if (count < 4) continue;
                        var p1 = xyzs[i];
                        int j2 = i + 1 >= xyzs.Count ? i + 1 - count : i + 1;
                        var p2 = xyzs[j2];
                        int j3 = i + 2 >= xyzs.Count ? i + 2 - count : i + 2;
                        var p3 = xyzs[j3];
                        int j4 = i + 3 >= xyzs.Count ? i + 3 - count : i + 3;
                        var p4 = xyzs[j4];
                        if (p2.IsEqual(p3) || p1.DistanceTo(p2) < tolerance || p3.DistanceTo(p4) < tolerance)
                            continue;
                        Curve curve1 = Line.CreateBound(p1, p2);
                        Curve curve2 = Line.CreateBound(p3, p4);
                        XYZ intersection = curve1.GetIntersection(curve2);
                        if (intersection == null) continue;
                        xyzs[j2] = intersection;
                        xyzs[j3] = intersection;
                    }

                    #endregion

                    //添加起始点，形成闭合区域
                    if (xyzs.Any())
                        xyzs.Add(xyzs[0]);

                    //整理数据
                    Polygon outLine = new Polygon();
                    var usePoints = xyzs.Select(xyz => BimConvert.ConvertToXYZ(xyz)).ToList();
                    outLine.AddRange(usePoints.GetRange(0, usePoints.Count - 1));
                    StandardUtil.ArrangeLoop(outLine);
                    outlines.Add(outLine);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }

            return outlines;
        }

        private static double m_RedundancySpace = 10d;
        /// <summary>
        /// 获取平行墙，平行且投影有重叠部分
        /// </summary>
        /// <param name="wall"></param>
        /// <returns></returns>
        public static List<Wall> GetParallelWalls(Wall wall)
        {
            List<Wall> walls = new List<Wall>();
            walls.Add(wall);
            var doc = wall.Document;
            var allWalls = doc.GetElements<Wall>();
            var curve = wall.GetLocationCurve();
            var parallelWalls = allWalls.Where(t => t.GetLocationCurve().IsParallel(curve));

            int len = walls.Count;
            do
            {
                len = walls.Count;
                foreach (Wall parallelWall in parallelWalls)
                {
                    var curve1 = parallelWall.GetLocationCurve();
                    var startP11 = CurveExtend.StartPoint(curve1);
                    var endP11 = CurveExtend.EndPoint(curve1);
                    double width1 = parallelWall.Width.FromApi();
                    for (int i = 0; i < walls.Count; i++)
                    {
                        Wall referenceWall = walls[i];
                        if (walls.Contains(parallelWall)) continue;
                        var curve2 = referenceWall.GetLocationCurve();
                        var startP12 = CurveExtend.GetProjectPt(curve2, startP11);
                        var endP12 = CurveExtend.GetProjectPt(curve2, endP11);
                        var startP21 = CurveExtend.StartPoint(curve2);
                        var endP21 = CurveExtend.EndPoint(curve2);
                        var startP22 = CurveExtend.GetProjectPt(curve1, startP21);
                        var endP22 = CurveExtend.GetProjectPt(curve1, endP21);
                        //判断是否有重叠部分
                        if (startP12.IsOnCurve(curve2) || endP12.IsOnCurve(curve2) || startP22.IsOnCurve(curve1) ||
                            endP22.IsOnCurve(curve1))
                        {
                            double width2 = referenceWall.Width.FromApi();
                            double distacne = curve1.Distance(curve2).FromApi();
                            if (distacne < (width1 + width2) / 2.0 + m_RedundancySpace)
                                walls.Add(parallelWall);
                        }
                    }
                }
            } while (len != walls.Count);

            return walls;
        }
        /// <summary>
        /// 获取外廓后的点
        /// </summary>
        /// <param name="xyz">外廓点</param>
        /// <param name="verticalVector">外廓向量</param>
        /// <param name="segmentCurve">参考Curve</param>
        /// <param name="walls">平行墙集合</param>
        /// <returns></returns>
        public static XYZ GetOuterXYZ(XYZ xyz, XYZ verticalVector, Curve segmentCurve, List<Wall> walls)
        {
            XYZ outerXyz = xyz;
            double distance = 0;
            foreach (Wall wall in walls)
            {
                /*
                 * 注意幕墙，没有底面，直接取原始点xyz
                 */
                List<PlanarFace> faces = wall.GetBottomFaces();
                foreach (PlanarFace face in faces)
                {
                    foreach (CurveLoop curveLoop in face.GetEdgesAsCurveLoops())
                    {
                        foreach (Curve curve in curveLoop)
                        {
                            /*
                             * 1. 获取平行边
                             * 2. 平行边求投影，投影点形成向量为向外
                             * 3. 投影点在线上
                             */
                            if (!segmentCurve.IsParallel(curve))
                                continue;
                            var projectPt = curve.GetProjectPt(xyz);
                            var tempVector = (projectPt - xyz).Normalize();
                            if (projectPt.IsOnCurve(curve) && !projectPt.IsEqual(xyz) && tempVector.IsEqual(verticalVector))
                            {
                                var tempDistance = xyz.DistanceTo(projectPt);
                                if (tempDistance >= distance)
                                {
                                    distance = tempDistance;
                                    outerXyz = projectPt;
                                }
                            }
                            else
                            {
                                /*
                                 * 无投影点，给个默认投影点的情况
                                 * 处理斜边投影不到的情况边上的情况,
                                 * 目前任意取了一个，多个取最近的？？
                                 */
                                if (outerXyz == null || outerXyz.IsEqual(xyz))
                                    outerXyz = projectPt;
                            }
                        }
                    }
                }
            }

            return outerXyz;
        }
    }
}
