using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using uBIMEarthTools.Model;

namespace uBIMEarthTools.Utils
{
    public class Common
    {
        /// <summary>
        /// 三角剖分
        /// </summary>
        /// <param name="boreholeList">钻孔列表</param>
        /// <param name="tolerance">排除过长三角形边边长</param>
        /// <returns>钻孔编号索引</returns>
        public static List<int[]> Delaunay(List<Borehole> boreholeList, double tolerance)
        {
            List<int[]> list = new List<int[]>();
            List<XYZ> xyzList = boreholeList.Select(x => new XYZ(x.ValueList[0].Point.X, x.ValueList[0].Point.Y, 0)).ToList();

            TriangleNet.Geometry.Polygon polygon = new TriangleNet.Geometry.Polygon(boreholeList.Count);

            foreach (XYZ item in xyzList)
            {
                polygon.Add(new TriangleNet.Geometry.Vertex(item.X, item.Y));
            }
            TriangleNet.Mesh mesh = (TriangleNet.Mesh)TriangleNet.Geometry.ExtensionMethods.Triangulate(polygon);

            foreach (TriangleNet.Topology.Triangle item in mesh.Triangles)
            {
                if (IsCollinear(item) || MaxEdge(item) > tolerance)
                    continue;
                int[] index = new int[3];
                index[0] = item.GetVertexID(0);
                index[1] = item.GetVertexID(1);
                index[2] = item.GetVertexID(2);
                list.Add(index);
            }
            return list;
        }


        #region MyRegion
        //public static List<int[]> Delaunay(List<Borehole> boreholeList, double tolerance)
        //{
        //    List<int[]> list = new List<int[]>();
        //    List<XYZ> xyzList = boreholeList.Select(x => new XYZ(x.ValueList[0].Point.X, x.ValueList[0].Point.Y, 0)).ToList();

        //    TriangleNet.Geometry.Polygon polygon = new TriangleNet.Geometry.Polygon(boreholeList.Count);

        //    foreach (XYZ item in xyzList)
        //    {
        //        polygon.Add(new TriangleNet.Geometry.Vertex(item.X, item.Y));
        //    }
        //    TriangleNet.Mesh mesh = (TriangleNet.Mesh)TriangleNet.Geometry.ExtensionMethods.Triangulate(polygon);

        //    foreach (TriangleNet.Topology.Triangle item in mesh.Triangles)
        //    {
        //        if (IsCollinear(item) || MaxEdge(item) > tolerance)
        //            continue;
        //        int[] index = new int[3];
        //        index[0] = item.GetVertexID(0);
        //        index[1] = item.GetVertexID(1);
        //        index[2] = item.GetVertexID(2);
        //        list.Add(index);
        //    }
        //    return list;
        //} 
        #endregion


        /// <summary>
        /// 判断三角形三点是否接近共线
        /// </summary>
        /// <param name="triangle"></param>
        /// <returns></returns>
        public static bool IsCollinear(TriangleNet.Topology.Triangle triangle)
        {
            XYZ p0 = new XYZ(triangle.GetVertex(0).X, triangle.GetVertex(0).Y, 0);
            XYZ p1 = new XYZ(triangle.GetVertex(1).X, triangle.GetVertex(1).Y, 0);
            XYZ p2 = new XYZ(triangle.GetVertex(2).X, triangle.GetVertex(2).Y, 0);
            Line line1 = Line.CreateUnbound(p0, p1 - p0);
            Line line2 = Line.CreateUnbound(p1, p2 - p1);
            Line line3 = Line.CreateUnbound(p2, p0 - p2);
            double d1 = line1.Project(p2).Distance / p0.DistanceTo(p1);
            double d2 = line2.Project(p0).Distance / p1.DistanceTo(p2);
            double d3 = line3.Project(p1).Distance / p2.DistanceTo(p0);
            if (Math.Min(Math.Min(d1, d2), d3) < 0.02)
                return true;
            else
                return false;
        }

        /// <summary>
        /// 三角形最长边长度
        /// </summary>
        /// <param name="triangle"></param>
        /// <returns></returns>
        public static double MaxEdge(TriangleNet.Topology.Triangle triangle)
        {
            XYZ p0 = new XYZ(triangle.GetVertex(0).X, triangle.GetVertex(0).Y, 0);
            XYZ p1 = new XYZ(triangle.GetVertex(1).X, triangle.GetVertex(1).Y, 0);
            XYZ p2 = new XYZ(triangle.GetVertex(2).X, triangle.GetVertex(2).Y, 0);
            return Math.Max(Math.Max(p0.DistanceTo(p1), p1.DistanceTo(p2)), p0.DistanceTo(p2));
        }

        public static int IsTypeEquals(List<Borehole> boreholeList)
        {
            List<string> typeList = boreholeList.Select(x => x.CurrentType).ToList();
            if (typeList[0].Equals(typeList[1]) && typeList[0].Equals(typeList[2]))
                return 1;
            else if (typeList[0].Equals(typeList[1]) && !typeList[0].Equals(typeList[2]))
                return 2;
            else if (typeList[0].Equals(typeList[2]) && !typeList[0].Equals(typeList[1]))
                return 3;
            else if (typeList[1].Equals(typeList[2]) && !typeList[0].Equals(typeList[2]))
                return 4;
            else
                return 0;
        }

        /// <summary>
        /// 点1在XY平面(忽略Z值)绕中心点逆时针旋转到点2的弧度[0,2Π]
        /// </summary>
        /// <param name="firstPoint"></param>
        /// <param name="centerPoint"></param>
        /// <param name="lastPoint"></param>
        /// <returns></returns>
        public static double AngleOfThreePoint(XYZ firstPoint, XYZ centerPoint, XYZ lastPoint)
        {
            XYZ vector1 = firstPoint - centerPoint;
            XYZ vector2 = lastPoint - centerPoint;
            double angle = Math.Atan2(vector2.Y, vector2.X) - Math.Atan2(vector1.Y, vector1.X);
            if (angle < 0)
            {
                return angle += Math.PI * 2;
            }
            else if (angle == 0)
            {
                return angle = Math.PI * 2;
            }
            else
                return angle;
        }

        public static double PointToLinePedal(XYZ lineFirstPoint, XYZ lineSecondPoint, XYZ point)
        {
            double A = lineSecondPoint.Y - lineFirstPoint.Y;
            double B = lineSecondPoint.X - lineFirstPoint.X;
            double C = lineSecondPoint.X * lineFirstPoint.Y - lineFirstPoint.X * lineSecondPoint.Y;
            return (A * point.X + B * point.Y + C) / Math.Sqrt(A * A + B * B);
        }



        public static double PointToLineDistance(XYZ lineFirstPoint, XYZ lineSecondPoint, XYZ point)
        {
            double A = lineSecondPoint.Y - lineFirstPoint.Y;
            double B = lineSecondPoint.X - lineFirstPoint.X;
            double C = lineSecondPoint.X * lineFirstPoint.Y - lineFirstPoint.X * lineSecondPoint.Y;
            return (A * point.X + B * point.Y + C) / Math.Sqrt(A * A + B * B);
        }

        /// <summary>
        /// 创建材质
        /// </summary>
        /// <param name="doc">Autodesk.Revit.DB.Document</param>
        /// <param name="name">材质名称</param>
        /// <returns></returns>
        public static Material CreateMaterial(Document doc, string name)
        {
            Material material = null;
            ElementId materialId = Material.Create(doc, name);
            material = doc.GetElement(materialId) as Material;
            StructuralAsset strucAsset = new StructuralAsset("uBIM", StructuralAssetClass.Undefined);
            strucAsset.Behavior = StructuralBehavior.Isotropic;
            PropertySetElement propertySet = PropertySetElement.Create(doc, strucAsset);
            propertySet.get_Parameter(BuiltInParameter.PROPERTY_SET_NAME).Set(name);
            propertySet.get_Parameter(BuiltInParameter.PHY_MATERIAL_PARAM_CLASS).Set((int)StructuralMaterialType.Undefined);
            propertySet.get_Parameter(BuiltInParameter.PHY_MATERIAL_PARAM_UNIT_WEIGHT).Set(232);
            material.SetMaterialAspectByPropertySet(Autodesk.Revit.DB.MaterialAspect.Structural, propertySet.Id);
            return material;
        }

        /// <summary>
        /// 创建材质
        /// </summary>
        /// <param name="doc">Autodesk.Revit.DB.Document</param>
        /// <param name="materialNameList">材质名称列表</param>
        /// <returns></returns>
        public static IList<Element> CreateMaterialElements(Document doc, List<string> materialNameList)
        {
            Transaction transaction = new Transaction(doc);
            IList<Element> mtset = new FilteredElementCollector(doc)
                .WherePasses(new ElementClassFilter(typeof(Material))).ToElements();
            transaction.Start("Create Material");
            foreach (string type in materialNameList)
            {
                if (mtset.Select(x => x.Name).ToList().Contains(type))
                    continue;
                else
                {
                    Material material = CreateMaterial(doc, type);
                    Random randomNumFirst = new Random((int)DateTime.Now.Ticks);
                    System.Threading.Thread.Sleep(randomNumFirst.Next(50));
                    Random randomNumSencond = new Random((int)DateTime.Now.Ticks);
                    int intRed = randomNumFirst.Next(256);
                    int intGreen = randomNumSencond.Next(256);
                    int intBlue = (intRed + intGreen > 400) ? 0 : 400 - intRed - intGreen;
                    intBlue = (intBlue > 255) ? 255 : intBlue;
                    material.Color = new Color((byte)intRed, (byte)intGreen, (byte)intBlue);
                    material.MaterialClass = "地质材质";
                    mtset.Add(material);
                }
            }
            transaction.Commit();
            return mtset;
        }

        /// <summary>
        /// Transaction设置
        /// </summary>
        /// <param name="trans">Transaction</param>
        public static void SetTransaction(Transaction trans)
        {
            FailureHandlingOptions failureHandlingOptions = trans.GetFailureHandlingOptions();
            FailureHandler failureHandler = new FailureHandler();
            failureHandlingOptions.SetFailuresPreprocessor(failureHandler);
            failureHandlingOptions.SetClearAfterRollback(true);
            trans.SetFailureHandlingOptions(failureHandlingOptions);
        }

        /// <summary>
        /// 求某点处的textnote，在textnote_list范围内 
        /// </summary>
        /// <param name="p">寻找点</param>
        /// <param name="textnoteList">TextNote列表</param>
        /// <param name="tolerance">误差范围,单位mm</param>
        /// <returns>textnote</returns>
        public static TextNote Textnote_At_Point(XYZ p, List<TextNote> textnoteList, double tolerance)
        {
            TextNote tn = null;
            XYZ newXyz = new XYZ(p.X, p.Y, textnoteList[0].Coord.Z);
            foreach (TextNote tntmp in textnoteList)
            {
                if (Math.Round(tntmp.Coord.DistanceTo(newXyz), 5) < (tolerance / 304.8))
                {
                    tn = tntmp;
                }
            }
            return tn;
        }
        public static void Swap<T>(ref T x, ref T y)
        {
            T temp = x;
            x = y;
            y = temp;
        }
    }
}
