using Autodesk.Revit.DB;
using HelixToolkit.Wpf.SharpDX;
using SharpDX;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Input;
using uBIMEarthTools.Core;
using uBIMEarthTools.Model;
using uBIMEarthTools.Utils;

namespace uBIMEarthTools.Commands.Monitor
{
    internal class MonitorViewModel
    {
        public IEffectsManager EffectsManager { get; set; }

        public Camera Camera { get; set; }
        public bool EnableSwapChainRendering { get; set; }

        public bool EnableD2DRendering { get; set; }
        public ObservableElement3DCollection Models { get; set; }
        public ICommand OpenCommand { get; set; }

        public MonitorViewModel()
        {
            this.Models = new ObservableElement3DCollection();

            this.EffectsManager = new DefaultEffectsManager();
            this.Camera = new PerspectiveCamera() { NearPlaneDistance = 0.1, FarPlaneDistance = 10000000 };
            this.EnableSwapChainRendering = true;
            this.EnableD2DRendering = false;
            OpenCommand = new DelegateCommand() { ExecuteCommand = OpenXlsx };
        }

        private void OpenXlsx(object obj)
        {
            #region 数据读取

            #region 数据文件读取路径
            string pathstring = string.Empty;
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Title = "选择Excel文件…";
            openFileDialog.Filter = "Excel文件|*.xlsx";
            openFileDialog.RestoreDirectory = false;
            openFileDialog.FilterIndex = 1;
            if (openFileDialog.ShowDialog() == DialogResult.OK)
                pathstring = openFileDialog.FileName;
            else
                return;
            #endregion

            Stopwatch stopwatch2 = new Stopwatch();
            stopwatch2.Start();
            List<Borehole> boreholeList = ExcelHelper.GetDataFromExcel(pathstring);
            stopwatch2.Stop();
            #endregion

            #region 数据有效性检测
            string abc = string.Empty;
            foreach (var borehole in boreholeList)
            {
                for (int i = 0; i < borehole.ValueList.Count - 2; i++)
                {
                    if (borehole.ValueList[i].Point.Z < borehole.ValueList[i + 1].Point.Z)
                    {
                        abc += borehole.Name + ",";
                        break;
                    }
                }
            }
            if (!string.IsNullOrEmpty(abc))
            {
                MessageBox.Show(abc);
            }
            #endregion

            #region 拟合
            boreholeList.ForEach(x => x.Interpolation(2));

            List<int[]> tris = Common.Delaunay(boreholeList, 10000000 / 304.8);
            var topList = boreholeList.Select(x =>
            {
                var p = x.ValueList.First();
                return new Point2D() { X = p.Point.X, Y = p.Point.Y, Value = p.Point.Z };
            }).ToList();
            var topKriging = new IDW<Point2D>(topList, 2);

            var bottomList = boreholeList.Select(x =>
            {
                var p = x.ValueList.Last();
                return new Point2D() { X = p.Point.X, Y = p.Point.Y, Value = p.Point.Z };
            }).ToList();
            var bottomKriging = new IDW<Point2D>(bottomList, 2);

            var typeValue = new Dictionary<string, int>();
            var valueType = new Dictionary<int, string>();
            var types = boreholeList.SelectMany(x => x.ValueList.Select(p => p.Type)).Distinct().ToList();
            for (int i = 0; i < types.Count; i++)
            {
                typeValue.Add(types[i], i + 1);
                valueType.Add(i + 1, types[i]);
            }
            var boreholeKriging = new IDW<Point3D>(boreholeList.SelectMany(b => b.ValueList.Select(p => new Point3D() { X = p.Point.X, Y = p.Point.Y, Z = p.Point.Z, Value = typeValue[p.Type] })).ToList(), 2);

            List<Borehole> boreholes1 = new List<Borehole>();
            tris.AsParallel().ForAll(item =>
            {
                Func<XYZ, Borehole> func = (p =>
                {
                    var topZ = topKriging.Interpolate(new Point2D() { X = p.X, Y = p.Y });
                    var bottomZ = bottomKriging.Interpolate(new Point2D() { X = p.X, Y = p.Y });
                    var plus = (topZ - bottomZ) / 10;
                    var geologyLayers = new List<GeologyLayer>();
                    for (int i = 0; i < 10; i++)
                    {
                        var p3d = new Point3D() { X = p.X, Y = p.Y, Z = topZ - i * plus };
                        var value = boreholeKriging.Interpolate2(p3d);
                        geologyLayers.Add(new GeologyLayer(valueType[(int)Math.Round(value)], new Autodesk.Revit.DB.XYZ(p3d.X, p3d.Y, p3d.Z)));
                    }
                    return new Borehole("", geologyLayers);
                });

                var v1 = boreholeList.ElementAt(item[1]).ValueList[0].Point - boreholeList.ElementAt(item[0]).ValueList[0].Point;
                var v2 = boreholeList.ElementAt(item[2]).ValueList[0].Point - boreholeList.ElementAt(item[0]).ValueList[0].Point;
                var originP = boreholeList.ElementAt(item[0]).ValueList[0].Point;
                var triangle = new List<XYZ>() { boreholeList.ElementAt(item[0]).ValueList[0].Point,
                                                boreholeList.ElementAt(item[1]).ValueList[0].Point,
                                                boreholeList.ElementAt(item[2]).ValueList[0].Point };
                var seg = 20;
                v1 = v1 / seg;
                v2 = v2 / seg;
                for (int i = 0; i < seg; i++)
                {
                    for (int j = 0; j < seg; j++)
                    {
                        if (i == j && i == 0) { continue; }
                        var xyz = originP + v1 * i + v2 * j;
                        if (IsInPolygon(xyz, triangle))
                        {
                            boreholes1.Add(func(xyz));
                        }
                    }
                }
            });
            //foreach (int[] item in tris)
            //{

            //}
            #endregion
            boreholeList.AddRange(boreholes1);

            #region 计算地质块
            List<int[]> triIndexList = Common.Delaunay(boreholeList, 10000000 / 304.8);
            List<GeologyBlock> geologyBlocks = new List<GeologyBlock>();
            foreach (int[] item in triIndexList)
            {
                List<Borehole> boreholes = new List<Borehole>() {
                    new Borehole(boreholeList.ElementAt(item[0])),
                    new Borehole(boreholeList.ElementAt(item[1])),
                    new Borehole(boreholeList.ElementAt(item[2]))
                };

                bool topToBottom = true;
                while (boreholes[0].StartIndex != boreholes[0].EndIndex
                       || boreholes[1].StartIndex != boreholes[1].EndIndex
                       || boreholes[2].StartIndex != boreholes[2].EndIndex)
                {
                    if (topToBottom)
                    {
                        boreholes[0].CurrentType = boreholes[0].StartIndex != boreholes[0].EndIndex ? boreholes[0].ValueList[boreholes[0].StartIndex + 1].Type : "Last1";
                        boreholes[1].CurrentType = boreholes[1].StartIndex != boreholes[1].EndIndex ? boreholes[1].ValueList[boreholes[1].StartIndex + 1].Type : "Last2";
                        boreholes[2].CurrentType = boreholes[2].StartIndex != boreholes[2].EndIndex ? boreholes[2].ValueList[boreholes[2].StartIndex + 1].Type : "Last3";
                        boreholes = boreholes.OrderByDescending(x => x.ValueList[x.StartIndex].Point.Z).ToList();
                    }
                    else
                    {
                        boreholes[0].CurrentType = boreholes[0].StartIndex != boreholes[0].EndIndex ? boreholes[0].ValueList[boreholes[0].EndIndex].Type : "Last1";
                        boreholes[1].CurrentType = boreholes[1].StartIndex != boreholes[1].EndIndex ? boreholes[1].ValueList[boreholes[1].EndIndex].Type : "Last2";
                        boreholes[2].CurrentType = boreholes[2].StartIndex != boreholes[2].EndIndex ? boreholes[2].ValueList[boreholes[2].EndIndex].Type : "Last3";
                        boreholes = boreholes.OrderBy(x => x.ValueList[x.EndIndex].Point.Z).ToList();
                    }
                    DataListSort(boreholes);
                    GeologyCompute.CreateGeologyBlock(boreholes, geologyBlocks, topToBottom);
                    topToBottom = !topToBottom;
                }

                // break;
            }
            #endregion


            #region 显示
            var colors = new Dictionary<string, Color4>();
            Random random = new Random();

            foreach (var item in geologyBlocks.Select(x => x.Type).Distinct())
            {
                colors.Add(item, PhongMaterials.ToColor(random.Next(0, 255) / 255f, random.Next(0, 255) / 255f, random.Next(0, 255) / 255f));
            }

            var origin = new SharpDX.Vector3((float)geologyBlocks.First().FLeftUp.X, (float)geologyBlocks.First().FLeftUp.Z, -(float)geologyBlocks.First().FLeftUp.Y);
            foreach (var item in geologyBlocks.GroupBy(g => g.Type))
            {
                var index = 0;
                List<SharpDX.Vector3> vector3s = new List<SharpDX.Vector3>();
                List<int> indices = new List<int>();
                foreach (var x in item)
                {
                    vector3s.Add(new SharpDX.Vector3((float)x.FLeftUp.X, (float)x.FLeftUp.Z, -(float)x.FLeftUp.Y));                      //0
                    vector3s.Add(new SharpDX.Vector3((float)x.FLeftBottom.X, (float)x.FLeftBottom.Z, -(float)x.FLeftBottom.Y));          //1
                    vector3s.Add(new SharpDX.Vector3((float)x.FRightUp.X, (float)x.FRightUp.Z, -(float)x.FRightUp.Y));                   //2
                    vector3s.Add(new SharpDX.Vector3((float)x.FRightBottom.X, (float)x.FRightBottom.Z, -(float)x.FRightBottom.Y));       //3
                    vector3s.Add(new SharpDX.Vector3((float)x.ELeftUp.X, (float)x.ELeftUp.Z, -(float)x.ELeftUp.Y));                      //4
                    vector3s.Add(new SharpDX.Vector3((float)x.ELeftBottom.X, (float)x.ELeftBottom.Z, -(float)x.ELeftBottom.Y));          //5
                    vector3s.Add(new SharpDX.Vector3((float)x.ERightUp.X, (float)x.ERightUp.Z, -(float)x.ERightUp.Y));                   //6
                    vector3s.Add(new SharpDX.Vector3((float)x.ERightBottom.X, (float)x.ERightBottom.Z, -(float)x.ERightBottom.Y));          //7

                    indices.Add(0 + index); indices.Add(2 + index); indices.Add(6 + index);
                    indices.Add(0 + index); indices.Add(6 + index); indices.Add(4 + index);
                    indices.Add(0 + index); indices.Add(3 + index); indices.Add(2 + index);
                    indices.Add(3 + index); indices.Add(0 + index); indices.Add(1 + index);
                    indices.Add(6 + index); indices.Add(2 + index); indices.Add(3 + index);
                    indices.Add(6 + index); indices.Add(3 + index); indices.Add(7 + index);

                    indices.Add(0 + index); indices.Add(4 + index); indices.Add(1 + index);
                    indices.Add(1 + index); indices.Add(4 + index); indices.Add(5 + index);
                    indices.Add(4 + index); indices.Add(6 + index); indices.Add(7 + index);
                    indices.Add(4 + index); indices.Add(7 + index); indices.Add(5 + index);
                    indices.Add(5 + index); indices.Add(7 + index); indices.Add(1 + index);
                    indices.Add(1 + index); indices.Add(7 + index); indices.Add(3 + index);

                    index = vector3s.Count;
                }

                MeshGeometry3D me = new MeshGeometry3D();
                me.Positions = new Vector3Collection(vector3s.Select(p => p - origin));
                me.Indices = new IntCollection(indices);
                me.Normals = me.CalculateNormals();
                Models.Add(new MeshGeometryModel3D()
                {
                    Geometry = me,
                    FillMode = SharpDX.Direct3D11.FillMode.Solid,
                    Material = new PhongMaterial()
                    {
                        AmbientColor = colors[item.Key],
                        //  SpecularColor = new SharpDX.Color4(0.0225f, 0.0225f, 0.0225f, 1.0f),
                        //EmissiveColor = new SharpDX.Color4(0.0f, 0.0f, 0.0f, 1.0f),
                        SpecularShininess = 12.8f,
                        // DiffuseColor = colors[x.Type],
                    }
                });
            }

            #endregion

            InitCamera();
        }


        public void InitCamera()
        {
            if (Models.Any())
            {
                var maxbox = Models.Select(x => x.Bounds).Aggregate(BoundingBox.Merge);
                var size = maxbox.Size;
                var maxLength = size.X > size.Y ? size.X > size.Z ? size.X : size.Z : size.Y > size.Z ? size.Y : size.Z;
                var radius = Vector3.Distance(maxbox.Center, new Vector3(maxbox.Center.X + (maxLength / 2), maxbox.Center.Y + (maxLength / 2), maxbox.Center.Z + (maxLength / 2)));
                var vec = (maxbox.Center + new Vector3(radius, radius, radius));
                this.Camera.Position = vec.ToPoint3D();
                this.Camera.LookDirection = (maxbox.Center - vec).ToVector3D();
                this.Camera.UpDirection = new Vector3(0, 1, 0).ToVector3D();
                //this.Camera.ZoomExtents();
            }
        }
        private static void DataListSort(List<Borehole> boreholes)
        {
            if (boreholes.First().CurrentType.Contains("Last"))
            {
                boreholes.Add(boreholes.First());
                boreholes.RemoveAt(0);
                DataListSort(boreholes);
            }
            else
                return;
        }

        public static bool IsInPolygon(XYZ checkPoint, List<XYZ> polygonPoints)
        {
            bool inSide = false;
            int pointCount = polygonPoints.Count;
            XYZ p1, p2;
            for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)//第一个点和最后一个点作为第一条线，之后是第一个点和第二个点作为第二条线，之后是第二个点与第三个点，第三个点与第四个点...  
            {
                p1 = polygonPoints[i];
                p2 = polygonPoints[j];
                if (checkPoint.Y < p2.Y)
                {//p2在射线之上  
                    if (p1.Y <= checkPoint.Y)
                    {//p1正好在射线中或者射线下方  
                        if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) > (checkPoint.X - p1.X) * (p2.Y - p1.Y))//斜率判断,在P1和P2之间且在P1P2右侧  
                        {
                            //射线与多边形交点为奇数时则在多边形之内，若为偶数个交点时则在多边形之外。  
                            //由于inside初始值为false，即交点数为零。所以当有第一个交点时，则必为奇数，则在内部，此时为inside=(!inside)  
                            //所以当有第二个交点时，则必为偶数，则在外部，此时为inside=(!inside)  
                            inSide = (!inSide);
                        }
                    }
                }
                else if (checkPoint.Y < p1.Y)
                {
                    //p2正好在射线中或者在射线下方，p1在射线上  
                    if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) < (checkPoint.X - p1.X) * (p2.Y - p1.Y))//斜率判断,在P1和P2之间且在P1P2右侧  
                    {
                        inSide = (!inSide);
                    }
                }
            }
            return inSide;
        }

    }
}
