/*-------------------------------------------------------------------------
 * 功能描述：RevitUtil
 * 作者：xulisong
 * 创建时间： 2019/6/24 9:55:09
 * 版本号：v1.0
 *  -------------------------------------------------------------------------*/

using Autodesk.Revit.DB;
using SAGA.RevitUtils.Extends;
using SAGA.RevitUtils.Extends.Graphic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.Revit.DB.Mechanical;
using RevitToJBim.Extension;
using RevitToJBim.MBI;
using SAGA.DotNetUtils;
using Parameter = JBIM.Component.Parameter;
using SAGA.RevitUtils;
using SAGA.DotNetUtils.Extend;
using System.Configuration;

namespace RevitToJBim.Common
{
    public class RevitUtil
    {
        /// <summary>
        /// 获取FamilyInstance顶面轮廓
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static List<XYZ> GetTopPolygon(FamilyInstance fi)
        {
            List<XYZ> path = new List<XYZ>();
            var faces = fi.GetOriginalFaces(XYZ.BasisZ);
            var topface = faces.FirstOrDefault();
            //传递的path中没有重复点
            if (topface != null)
            {
                var curves = topface.GetCurves();
                for (int i = 0; i < curves.Count; i++)
                {
                    var current = curves[i];
                    var points = current.GetPoints();
                    points.RemoveAt(points.Count - 1);
                    path.AddRange(points);
                }
            }

            return path;
        }
        /// <summary>
        /// 获取FamilyInstance box底面轮廓
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static List<XYZ> GetBottomPolygon(FamilyInstance fi)
        {
            List<XYZ> path = new List<XYZ>();
            try
            {
                var box = fi.get_BoundingBox(null);
                if (box == null) return path;
                var boxMin = box.Min;
                var boxMax = box.Max;
                path.Add(boxMin);
                path.Add(boxMin.NewX(boxMax.X));
                path.Add(boxMin.NewX(boxMax.X).NewY(boxMax.Y));
                path.Add(boxMin.NewY(boxMax.Y));
                path.Add(boxMin);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            return path;
        }

        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="ele"></param>
        /// <param name="parameterNames"></param>
        /// <param name="hasEquipComponentCode"></param>
        /// <returns></returns>
        public static List<Parameter> GetParameters(Element ele, List<string> parameterNames, bool hasEquipComponentCode = false)
        {
            List<Parameter> parameters = new List<Parameter>();
            foreach (var parameterName in parameterNames)
            {
                var revitParameter = ele.GetParameter(parameterName);
                if (revitParameter != null)
                {
                    var parameter = new Parameter(ParameterUtil.FindParameterDefine(parameterName));
                    parameter.Value = revitParameter.AsString();
                    parameters.Add(parameter);
                }
            }
            if (hasEquipComponentCode)
            {
                var equipComponentCode = ConfigurationManager.AppSettings["EquipComponentCode"];
                var typeParameter = ele?.GetElementType()?.Parameters;
                foreach (Autodesk.Revit.DB.Parameter p in typeParameter)
                {
                    if (equipComponentCode.Equals(p.Definition.Name))
                    {
                        if (!p.HasValue || (p.AsString() == null && p.AsString().Length == 0)) continue;
                        var parameter = new Parameter(ParameterUtil.FindParameterDefine(equipComponentCode));
                        parameter.Value = p.AsString();
                        parameters.Add(parameter);
                    }
                }
            }

            return parameters;
        }
        /// <summary>
        /// 获取设备设施的参数
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static List<Parameter> GetFacilityParameters(FamilyInstance fi)
        {
            List<string> parameterNames = ConfigurationManager.AppSettings["FacilityParameters"].Split(',').ToList();
            List<Parameter> parameters = GetParameters(fi, parameterNames, true);
            return parameters;
        }
        /// <summary>
        /// 获取空间的参数
        /// </summary>
        /// <param name="space"></param>
        /// <returns></returns>
        public static List<Parameter> GetSpaceParameters(Element space)
        {
            List<string> parameterNames = ConfigurationManager.AppSettings["SpaceParameters"].Split(',').ToList(); 
            List<Parameter> parameters = GetParameters(space, parameterNames, false);
            return parameters;
        }
        /// <summary>
        /// 获取房间的参数
        /// </summary>
        /// <param name="room"></param>
        /// <returns></returns>
        public static List<Parameter> GetRoomParameters(Element room)
        {
            List<string> parameterNames = ConfigurationManager.AppSettings["RoomParameters"].Split(',').ToList();
            List<Parameter> parameters = GetParameters(room, parameterNames, false);
            return parameters;
        }
        /// <summary>
        /// 获取设备设施的参数
        /// </summary>
        /// <param name="space"></param>
        /// <returns></returns>
        public static List<Parameter> GetMEPCurveParameters(MEPCurve curve)
        {
            List<string> parameterNames = ConfigurationManager.AppSettings["MEPCurveParameters"].Split(',').ToList();
            List<Parameter> parameters = GetParameters(curve, parameterNames, false);
            return parameters;
        }

        /// <summary>
        /// 获取部件所关联的设备
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static Element GetEquipPartParent(Element element)
        {
            if (!element.IsEquipmentPart()) return null;
            string code = element.GetFamilyName()?.Substring(0, 4); ;
            if (code.IsNullOrEmpty()) return null;
            //构件所关联的设备
            var parentInst = element.Document.GetElements(new ElementIntersectsElementFilter(element))
                .FirstOrDefault(t => !t.Id.IsEqual(element.Id) && t.GetFamilyCode() == code);
            return parentInst;
        }
        public static ElementType GetElementType(Element element)
        {
            ElementId id = element.GetTypeId();
            if (id != null && id != ElementId.InvalidElementId)
            {
                Element elementType = element.Document.GetElement(id);
                return elementType as ElementType;
            }
            return null;
        }
        /// <summary>
        /// 获取门窗到墙线上的投影线
        /// </summary>
        /// <param name="fi"></param>
        /// <returns></returns>
        public static List<XYZ> GetWindowDoorLocation(FamilyInstance fi)
        {
            //取几何实体顶点信息，将点到指定方向做投影
            List<XYZ> xyzs = GetVertexPoints(fi);
            XYZ handDirection = (fi.Host as Wall)?.Orientation;
            if (handDirection != null)
            {
                handDirection = handDirection.CrossProduct(XYZ.BasisZ);
            }
            else if (handDirection == null)
            {
                handDirection = fi.HandOrientation;
            }
            var location = fi.GetLocationPoint();
            if (location == null)
            {
                location = xyzs.FirstOrDefault();
            }

            double min = 0, max = 0;
            XYZ minXYZ = XYZ.Zero, maxXYZ = XYZ.Zero;
            for (int i = 0; i < xyzs.Count; i++)
            {
                var usePoint = xyzs[i];
                var refDirection = usePoint - location;
                var offset = refDirection.DotProduct(handDirection);
                var projectPoint = location.OffsetPoint(handDirection, offset);
                #region 初始化逻辑
                if (i == 0)
                {
                    minXYZ = maxXYZ = projectPoint;
                    min = max = offset;
                }
                #endregion
                #region 判定逻辑
                else
                {
                    if (offset < min)
                    {
                        minXYZ = projectPoint;
                        min = offset;
                        continue;
                    }
                    if (max < offset)
                    {
                        maxXYZ = projectPoint;
                        max = offset;
                        continue;
                    }
                }
                #endregion
            }
            return new List<XYZ>() { minXYZ, maxXYZ };
        }
        /// <summary>
        /// 获取元素顶点集合
        /// </summary>
        /// <returns></returns>
        public static List<XYZ> GetVertexPoints(Element element)
        {
            List<XYZ> xyzs = new List<XYZ>();
            //string id = element.Id.ToString();
            //var solids = Extension.GeometryElementExtension.GetSolids(element, element.Document.GetUseView());
            //foreach (var solid in solids)
            //{
            //    foreach (Edge edge in solid.Edges)
            //    {
            //        xyzs.AddRange(edge.Tessellate());
            //    }
            //}
            var curves = Extension.GeometryElementExtension.GetGeometryObjects<Curve>(element, element.Document.GetUseView(), true);
            foreach (var curve in curves)
            {
                xyzs.AddRange(curve.Tessellate());
            }
            return xyzs.Distinct(new XyzEqualComparer()).ToList();
        }
        /// <summary>
        /// 获取墙的轮廓线
        /// </summary>
        /// <param name="wall"></param>
        /// <returns></returns>
        public static List<List<XYZ>> GetWallPolygon(Wall wall)
        {
            /* 方案1：
             * 1找到墙的定位线，
             * 2将定位线拆分成多段
             * 3按照宽度，将多段定位线生成闭合区域
             * 方案2：
             * 直接去墙的顶面，过滤相关face形成轮廓
             */
            List<List<XYZ>> polygons = new List<List<XYZ>>();
            #region 过滤墙的顶面         
            List<PlanarFace> faces = wall.GetBottomFaces();
            #endregion
            foreach (PlanarFace face in faces)
            {
                foreach (CurveLoop curveLoop in face.GetEdgesAsCurveLoops())
                {
                    if (curveLoop == null)
                    {
                        continue;
                    }
                    var polygon = curveLoop.GetPolygon();
                    if (polygon.Any())
                    {
                        polygons.Add(polygon);
                    }
                }
            }
            return polygons;
        }
    }
}
