using Autodesk.Revit.Creation;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using uBIMEarthTools.Core;
using uBIMEarthTools.Model;
using uBIMEarthTools.Utils;
using Line = Autodesk.Revit.DB.Line;

namespace uBIMEarthTools.Commands
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    public class GeologyModelCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            #region 变量
            UIDocument uidoc = commandData.Application.ActiveUIDocument;
            Autodesk.Revit.DB.Document doc = uidoc.Document;
            #endregion

            #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 Result.Cancelled;
            #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 计算地质块
            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;
                }
            }
            #endregion

            Stopwatch stopwatch = new Stopwatch();

            TransactionStatus status = TransactionStatus.Uninitialized;
            TransactionGroup transGroup = new TransactionGroup(doc, "地质建模");
            if (transGroup.Start() == TransactionStatus.Started)
            {


                #region 加载族,创建族类型
                List<string> typesList = ExcelHelper.GetGeologyType(pathstring).Select(x => "地质" + x.Item1 + "-" + x.Item2 + "层").ToList();


                #endregion

                #region 创建材质
                IList<Element> mtset = Common.CreateMaterialElements(doc, typesList);
                #endregion

                Transaction transaction = new Transaction(doc, "Create GeologyBlock");
                Common.SetTransaction(transaction);

                #region 创建钻孔标记
                transaction.Start("load the FamilySymbol");
                IEnumerable<FamilySymbol> familysymbols_bj = from elem in new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol))
                                                             let fstmp = elem as FamilySymbol
                                                             where fstmp.Name.Contains("钻孔标记")
                                                             select fstmp;
                if (!familysymbols_bj.Any())
                {
                    MessageBox.Show("请先加载配套的“钻孔标记”族。", "uBIM", MessageBoxButtons.OK);
                    status = transGroup.Assimilate();
                    return Result.Succeeded;
                }
                FamilySymbol fs_bj = doc.GetElement(familysymbols_bj.First().Id) as FamilySymbol;
                fs_bj.Activate();
                transaction.Commit();

                transaction.Start("Create Tag");

                var basePoint = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_ProjectBasePoint)
                    .FirstElement() as BasePoint;
                foreach (var borehole in boreholeList)
                {
                    var postitation = borehole.ValueList.First().Point;

                    //postitation = new XYZ(postitation.X - basePoint.SharedPosition.X,
                    //    postitation.Y - basePoint.SharedPosition.Y, postitation.Z);
                    //FamilyInstance fi_bj = doc.Create.NewFamilyInstance(borehole.ValueList.First().Point, fs_bj, StructuralType.NonStructural);
                    FamilyInstance fi_bj = doc.Create.NewFamilyInstance(postitation, fs_bj, StructuralType.NonStructural);
                    fi_bj.LookupParameter("孔号").Set(borehole.Name);
                }


                transaction.Commit();
                #endregion

                #region 创建自适应地质块
                //using (FilterProgressForm prf1 = new FilterProgressForm("生成地质模型", "{0} of " + geologyBlocks.Count.ToString(), geologyBlocks.Count))
                //{
                //    Family family = (new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel)
                //    .OfClass(typeof(FamilySymbol)).First(x => x.Name == "自适应地质块") as FamilySymbol).Family;
                //    Autodesk.Revit.DB.Document fdoc = doc.EditFamily(family);
                //    Transaction trans = new Transaction(fdoc);
                //    trans.Start("Create FamilySymbol");
                //    if (null != fdoc)
                //    {
                //        FamilyManager fm = fdoc.FamilyManager;
                //        foreach (string type in typesList)
                //        {
                //            try
                //            {
                //                FamilyType ft = fm.NewType(type);
                //            }
                //            catch
                //            {
                //                continue;
                //            }
                //        }

                //    }

                //    trans.Commit();
                //    LoadOpts loadOptions = new LoadOpts();
                //    family = fdoc.LoadFamily(doc, loadOptions);

                //    transaction.Start();
                //    //for (int i = 8; i < 9; i++)
                //    for (int i = 0; i < geologyBlocks.Count; i++)
                //    {
                //        FamilySymbol familySymbol = new FilteredElementCollector(doc)
                //            .OfCategory(BuiltInCategory.OST_GenericModel).OfClass(typeof(FamilySymbol))
                //            .First(x => x.Name == geologyBlocks[i].Type && (x as FamilySymbol).FamilyName == "自适应地质块") as FamilySymbol;

                //        if (!familySymbol.IsActive)
                //        {
                //            familySymbol.Activate();
                //        }

                //        FamilyInstance familyInstance =
                //            AdaptiveComponentInstanceUtils.CreateAdaptiveComponentInstance(doc, familySymbol);
                //        //调整自适应族位置
                //        IList<ElementId> plist = AdaptiveComponentInstanceUtils.GetInstancePlacementPointElementRefIds(familyInstance);
                //        ReferencePoint referencePoint = doc.GetElement(plist.ElementAt(0)) as ReferencePoint;
                //        ReferencePoint referencePoint1 = doc.GetElement(plist.ElementAt(1)) as ReferencePoint;
                //        ReferencePoint referencePoint2 = doc.GetElement(plist.ElementAt(2)) as ReferencePoint;
                //        ReferencePoint referencePoint3 = doc.GetElement(plist.ElementAt(3)) as ReferencePoint;
                //        ReferencePoint referencePoint4 = doc.GetElement(plist.ElementAt(4)) as ReferencePoint;
                //        ReferencePoint referencePoint5 = doc.GetElement(plist.ElementAt(5)) as ReferencePoint;
                //        ReferencePoint referencePoint6 = doc.GetElement(plist.ElementAt(6)) as ReferencePoint;
                //        ReferencePoint referencePoint7 = doc.GetElement(plist.ElementAt(7)) as ReferencePoint;
                //        referencePoint.Position = geologyBlocks[i].FLeftUp;
                //        referencePoint1.Position = geologyBlocks[i].FLeftBottom;
                //        referencePoint2.Position = geologyBlocks[i].FRightUp;
                //        referencePoint3.Position = geologyBlocks[i].FRightBottom;
                //        referencePoint4.Position = geologyBlocks[i].ELeftUp;
                //        referencePoint5.Position = geologyBlocks[i].ELeftBottom;
                //        referencePoint6.Position = geologyBlocks[i].ERightUp;
                //        referencePoint7.Position = geologyBlocks[i].ERightBottom;
                //        //调整自适应族参数
                //        familyInstance.LookupParameter("材质").Set(mtset.First(x => x.Name == familyInstance.Name).Id);
                //        familyInstance.LookupParameter("孔号一").Set(geologyBlocks[i].Num1);
                //        familyInstance.LookupParameter("孔号二").Set(geologyBlocks[i].Num2);
                //        familyInstance.LookupParameter("孔号三").Set(geologyBlocks[i].Num3);
                //        if (prf1.progressBar1.Tag.ToString() == "Cancel")
                //            break;
                //        prf1.Increment();
                //        System.Windows.Forms.Application.DoEvents();
                //    }
                //    transaction.Commit();
                //}
                #endregion

                stopwatch.Start();
                #region 创建常规模型地质块
                using (FilterProgressForm prf1 = new FilterProgressForm("生成地质模型", "{0} of " + geologyBlocks.Count.ToString(), geologyBlocks.Count))
                {
                    Family family = (new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel)
                       .OfClass(typeof(FamilySymbol)).First(x => x.Name == "常规模型地质块") as FamilySymbol).Family;
                    Autodesk.Revit.DB.Document fdoc = doc.EditFamily(family);
                    Transaction trans = new Transaction(fdoc);
                    trans.Start("Create FamilySymbol");
                    if (null != fdoc)
                    {
                        FamilyManager fm = fdoc.FamilyManager;
                        foreach (string type in typesList)
                        {
                            try
                            {
                                FamilyType ft = fm.NewType(type);
                            }
                            catch
                            {
                                continue;
                            }
                        }

                    }

                    trans.Commit();
                    LoadOpts loadOptions = new LoadOpts();
                    family = fdoc.LoadFamily(doc, loadOptions);

                    Level level = new FilteredElementCollector(doc)
                            .OfCategory(BuiltInCategory.OST_Levels).OfClass(typeof(Level))
                            .First() as Level;
                    double elevation = level.Elevation;

                    int index = 0;
                    //  var translation = new XYZ(basePoint.SharedPosition.X, basePoint.SharedPosition.Y, 0);
                    //foreach (var block in geologyBlocks)
                    //{
                    //    block.FLeftUp = block.FLeftUp - translation;
                    //    block.FLeftBottom = block.FLeftBottom - translation;
                    //    block.FRightUp = block.FRightUp - translation;
                    //    block.FRightBottom = block.FRightBottom - translation;
                    //    block.ELeftUp = block.ELeftUp - translation;
                    //    block.ELeftBottom = block.ELeftBottom - translation;
                    //    block.ERightUp = block.ERightUp - translation;
                    //    block.ERightBottom = block.ERightBottom - translation;
                    //}

                    while (index < geologyBlocks.Count)
                    {
                        List<FamilyInstanceCreationData> familyInstanceCreationDataList = new List<FamilyInstanceCreationData>();
                        List<GeologyParams> geologyParamsList = new List<GeologyParams>();

                        for (int i = 0; i < 100 && i < geologyBlocks.Count - index; i++)
                        {
                            transaction.Start();
                            Common.SetTransaction(transaction);
                            FamilySymbol familySymbol = new FilteredElementCollector(doc)
                                .OfCategory(BuiltInCategory.OST_GenericModel).OfClass(typeof(FamilySymbol))
                                .First(x => x.Name == geologyBlocks[index + i].Type) as FamilySymbol;

                            if (!familySymbol.IsActive)
                            {
                                familySymbol.Activate();
                            }

                            GeologyParams geologyParams = geologyBlocks[index + i].GetGeologyParams(elevation);
                            FamilyInstanceCreationData familyInstanceCreationData = new FamilyInstanceCreationData(geologyParams.Location, familySymbol, StructuralType.NonStructural);

                            familyInstanceCreationDataList.Add(familyInstanceCreationData);
                            geologyParamsList.Add(geologyParams);
                            transaction.Commit();
                        }
                        transaction.Start();
                        Common.SetTransaction(transaction);

                        List<ElementId> elementIdList = doc.Create.NewFamilyInstances2(familyInstanceCreationDataList).ToList();

                        transaction.Commit();

                        transaction.Start();
                        Common.SetTransaction(transaction);
                        for (int i = 0; i < elementIdList.Count; i++)
                        {

                            GeologyParams geologyParams = geologyParamsList[i];
                            FamilyInstance familyInstance = doc.GetElement(elementIdList[i]) as FamilyInstance;
                            familyInstance.Location.Rotate(
                                Line.CreateBound(geologyParams.Location, geologyParams.Location + new XYZ(0, 0, -1)),
                                geologyParams.Angle);
                            familyInstance.LookupParameter("材质")
                                .Set(mtset.First(x => x.Name == familyInstance.Name).Id);
                            familyInstance.LookupParameter("F面宽").Set(geologyParams.FWidth);
                            familyInstance.LookupParameter("F面左距").Set(geologyParams.FDistance);
                            familyInstance.LookupParameter("右底高").Set(geologyParams.FRightBottom);
                            familyInstance.LookupParameter("右边高").Set(geologyParams.FRightHeight);
                            familyInstance.LookupParameter("左底高").Set(geologyParams.FLeftBottom);
                            familyInstance.LookupParameter("左边高").Set(geologyParams.FLeftHeight);
                            familyInstance.LookupParameter("棱边右底高").Set(geologyParams.ERightBottom);
                            familyInstance.LookupParameter("棱边右边高").Set(geologyParams.ERightHeight);
                            familyInstance.LookupParameter("棱边左底高").Set(geologyParams.ELeftBottom);
                            familyInstance.LookupParameter("棱边左边高").Set(geologyParams.ELeftHeight);
                            familyInstance.LookupParameter("棱边垂距").Set(geologyParams.FEOffset);
                            familyInstance.LookupParameter("棱边左距").Set(geologyParams.EDistance);
                            familyInstance.LookupParameter("棱边长度").Set(geologyParams.EWidth);
                            familyInstance.LookupParameter("孔号一").Set(geologyBlocks[index].Num1);
                            familyInstance.LookupParameter("孔号二").Set(geologyBlocks[index].Num2);
                            familyInstance.LookupParameter("孔号三").Set(geologyBlocks[index].Num3);

                            index++;
                            if (prf1.progressBar1.Tag.ToString() == "Cancel")
                            {
                                transaction.RollBack();
                                return Result.Cancelled;
                            }

                            prf1.Increment();
                            System.Windows.Forms.Application.DoEvents();
                        }
                        transaction.Commit();
                    }

                }
                #endregion
                stopwatch.Stop();
            }

            status = transGroup.Assimilate();
            MessageBox.Show(stopwatch.Elapsed.ToString());
            return Result.Succeeded;
        }

        private static void DataListSort(List<Borehole> boreholes)
        {
            if (boreholes.First().CurrentType.Contains("Last"))
            {
                boreholes.Add(boreholes.First());
                boreholes.RemoveAt(0);
                DataListSort(boreholes);
            }
            else
                return;
        }
    }
}
