using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using uBIM.UI;
using uBIMEarthTools.MassEarthwork.Models;
using uBIMEarthTools.MassEarthwork.Views;

namespace uBIMEarthTools.Commands.MassEarthwork
{
    [Autodesk.Revit.Attributes.Transaction(Autodesk.Revit.Attributes.TransactionMode.Manual)]
    public class MassCutEverythingCommand : IExternalCommand
    {
        public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
        {
            var app = commandData.Application;
            var uidoc = app.ActiveUIDocument;
            var doc = uidoc.Document;

            List<FamilyInstance> masses = new List<FamilyInstance>();
            try
            {
                var massesRef = uidoc.Selection.PickObjects(ObjectType.Element, new MassSelectionFilter(), "请选择体量.");
                foreach (var item in massesRef)
                {
                    var mass = doc.GetElement(item) as FamilyInstance;
                    if (mass != null)
                        masses.Add(mass);
                }
            }
            catch
            {
                return Result.Failed;
            }

            FilteredElementCollector collector = new FilteredElementCollector(doc).OfClass(typeof(FamilyInstance)).OfCategory(BuiltInCategory.OST_GenericModel);

            // 体量solid
            var massesSolid = new List<Solid>();
            masses.ForEach(o =>
            {
                var s = GetSolid(o);
                if (s != null)
                {
                    massesSolid.Add(s);
                }

            });


            //var solid = solids[0];
            //for (int i = 1; i < solids.Count; i++)
            //{
            //    solid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, solids[i], BooleanOperationsType.Union);
            //}

            var familyInfos = new List<CustomFamilyInfo>();
            var dataInfos = new List<MassCutEverythingDataInfo>();

            ProgressWindow progressWindow = new ProgressWindow();
            var progressWindowViewModel = progressWindow.DataContext as ProgressViewModel;
            progressWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;

            var massIndex = 1;
            foreach (var mass in masses)
            {
                var massSolid = GetSolid(mass);
                var boudingBox = massSolid.GetBoundingBox();
                Outline myOutLn = new Outline(boudingBox.Min, boudingBox.Max);

                ElementIntersectsSolidFilter filter = new ElementIntersectsSolidFilter(massSolid);

                var instances = collector.WherePasses(filter).ToElements().Where(o => o.Name.Contains("地质") || o.Name.Contains("地形")).Cast<FamilyInstance>().ToList();

                //uidoc.Selection.SetElementIds(instances.Select(o => o.Id).ToList());

                var groupByInstance = instances.GroupBy(o => o.Name);

                var dictionary = new Dictionary<string, List<KeyValuePair<FamilyInstance, Solid>>>();
                foreach (var item in groupByInstance)
                {
                    var solids = GetInstanceSolids(item.ToList());
                    if (solids.Any())
                    {
                        dictionary.Add(item.Key, solids);
                    }
                }

                progressWindow.Title = $"碰撞检查{massIndex}……";
                progressWindowViewModel.MaxValue = dictionary.Select(o => o.Value.ToList().Count).Sum();
                progressWindowViewModel.CurrentValue = 0;
                progressWindow.Show();
                foreach (var item in dictionary)
                {
                    var count = 0;
                    double volumeOfIntersection = 0.0;
                    foreach (var solid in item.Value)
                    {
                        progressWindowViewModel.CurrentValue++;
                        ProgressWindow.DoEvents();
                        if (progressWindowViewModel.State == ProgressStatus.Stop)
                        {
                            progressWindow.Close();
                            return Result.Succeeded;
                        }
                        try
                        {


                            Solid intersection = BooleanOperationsUtils.ExecuteBooleanOperation(solid.Value, massSolid, BooleanOperationsType.Intersect);
                            Solid difference = BooleanOperationsUtils.ExecuteBooleanOperation(solid.Value, massSolid, BooleanOperationsType.Difference);

                            if (intersection.Volume > 0 && Math.Round(intersection.Volume, 5) != Math.Round(solid.Value.Volume, 5))
                            {
                                var intersectionSymbol = doc.GetElement(CreateFamily(doc, intersection, "常规模型地质块" + Guid.NewGuid(), item.Key).GetFamilySymbolIds().First()) as FamilySymbol;
                                var differenceSymbol = doc.GetElement(CreateFamily(doc, difference, "常规模型地质块" + Guid.NewGuid(), item.Key).GetFamilySymbolIds().First()) as FamilySymbol;
                                familyInfos.Add(new CustomFamilyInfo
                                {
                                    MassId = mass.Id.IntegerValue,
                                    Instance = solid.Key,
                                    SymbolIntersection = intersectionSymbol,
                                    SymbolDifference = differenceSymbol
                                });

                                volumeOfIntersection += intersection.Volume;
                                count++;
                            }
                            else
                            {
                                using (Transaction transaction = new Transaction(doc, "create"))
                                {
                                    transaction.Start();
                                    solid.Key.LookupParameter("注释").Set(mass.Id.IntegerValue.ToString());
                                    transaction.Commit();
                                }
                            }
                        }
                        catch
                        {

                        }
                    }
                    volumeOfIntersection /= 35.315;
                    volumeOfIntersection = Math.Round(volumeOfIntersection, 3);
                    MassCutEverythingDataInfo dataInfo = dataInfos.SingleOrDefault(o => o.Name == item.Key);
                    if (dataInfo == null)
                    {
                        dataInfo = new MassCutEverythingDataInfo
                        {
                            Name = item.Key,
                            Count = count,
                            Volume = volumeOfIntersection,
                            Unit = "m³"
                        };
                    }
                    else
                    {
                        dataInfo.Count += count;
                        dataInfo.Volume += volumeOfIntersection;
                    }
                    dataInfos.Add(dataInfo);
                }
                massIndex++;
            }

            dataInfos.Add(new MassCutEverythingDataInfo
            {
                Name = "总计",
                Unit = "m³",
                Count = dataInfos.Select(o => o.Count).Sum(),
                Volume = dataInfos.Select(o => o.Volume).Sum()
            });

            progressWindow.Title = "剪切地形……";
            progressWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
            progressWindowViewModel.MaxValue = familyInfos.Count();
            progressWindowViewModel.CurrentValue = 0;
            progressWindow.Show();
            using (Transaction transaction = new Transaction(doc, "create"))
            {
                transaction.Start();
                foreach (var item in familyInfos)
                {
                    progressWindowViewModel.CurrentValue++;
                    ProgressWindow.DoEvents();
                    if (progressWindowViewModel.State == ProgressStatus.Stop)
                    {
                        progressWindow.Close();
                        transaction.Commit();
                        return Result.Succeeded;
                    }
                    if (!item.SymbolIntersection.IsActive)
                    {
                        item.SymbolIntersection.Activate();
                    }
                    if (!item.SymbolDifference.IsActive)
                    {
                        item.SymbolDifference.Activate();
                    }

                    var intersectionInstance = doc.Create.NewFamilyInstance(new XYZ(), item.SymbolIntersection, doc.GetElement(item.Instance.LevelId) as Level, StructuralType.NonStructural);

                    intersectionInstance.LookupParameter("材质").Set(item.Instance.LookupParameter("材质").AsElementId());

                    var differenceInstance = doc.Create.NewFamilyInstance(new XYZ(), item.SymbolDifference, doc.GetElement(item.Instance.LevelId) as Level, StructuralType.NonStructural);

                    differenceInstance.LookupParameter("材质").Set(item.Instance.LookupParameter("材质").AsElementId());
                    doc.Delete(item.Instance.Id);

                    //differenceInstance.LookupParameter("注释").Set(item.MassId.ToString());
                    intersectionInstance.LookupParameter("注释").Set(item.MassId.ToString());
                }

                transaction.Commit();
            }

            progressWindow.Close();

            MassCutEverythingWindow window = new MassCutEverythingWindow(dataInfos);
            window.ShowDialog();

            return Result.Succeeded;
        }


        public List<Solid> GetSolids(List<FamilyInstance> instances)
        {
            var solids = new List<Solid>();
            foreach (var instance in instances)
            {
                Options geomOptions = new Options();
                geomOptions.ComputeReferences = true;
                if (instance == null)
                {
                    continue;
                }
                GeometryElement geoElement = instance.get_Geometry(new Options(geomOptions));
                foreach (var geometry in geoElement)
                {
                    if (geometry is GeometryInstance geoInstance)
                    {
                        var go = geoInstance.GetInstanceGeometry();
                        foreach (GeometryObject item in go)
                        {
                            var solid = item as Solid;
                            if (solid != null && solid.Volume != 0)
                            {
                                solids.Add(solid);
                                break;
                            }
                        }
                    }
                }
            }

            return solids;
        }

        public List<KeyValuePair<FamilyInstance, Solid>> GetInstanceSolids(List<FamilyInstance> instances)
        {
            var result = new List<KeyValuePair<FamilyInstance, Solid>>();
            foreach (var instance in instances)
            {
                Options geomOptions = new Options();
                geomOptions.ComputeReferences = true;
                if (instance == null)
                {
                    continue;
                }

                GeometryElement geoElement = instance.get_Geometry(new Options(geomOptions));
                foreach (var geometry in geoElement)
                {
                    if (geometry is GeometryInstance geoInstance)
                    {
                        var go = geoInstance.GetInstanceGeometry();
                        foreach (GeometryObject item in go)
                        {
                            var solid = item as Solid;
                            if (solid != null && solid.Volume != 0)
                            {
                                result.Add(new KeyValuePair<FamilyInstance, Solid>(instance, solid));
                                break;
                            }
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 创建族
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="loop"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public Family CreateFamily(Document doc, Solid solid, string name, string symbolName)
        {
            var assembly = Directory.GetParent(Assembly.GetExecutingAssembly().Location).FullName;
            string rftPath = Path.Combine(assembly, "公制常规模型.rft");
            var app = doc.Application;

            //新建族文档
            var familyDoc = app.NewFamilyDocument(rftPath);

            var trans = new Transaction(familyDoc, "convert");
            trans.Start();

            familyDoc.FamilyManager.NewType(symbolName);

            var result = FreeFormElement.Create(familyDoc, solid);

            var vo = AddParameter(familyDoc.FamilyManager, "体积", ParameterType.Text, false);
            var materialParameter = AddParameter(familyDoc.FamilyManager, "材质", ParameterType.Material, true);

            familyDoc.FamilyManager.AssociateElementParameterToFamilyParameter(result.get_Parameter(BuiltInParameter.MATERIAL_ID_PARAM), materialParameter);

            trans.Commit();


            //判断是否存在这个族文件如果有的话直接删除
            try
            {
                var fullPathName = doc.PathName;
                var index = fullPathName.LastIndexOf('\\');
                var path = fullPathName.Substring(0, index) + "\\";

                if (File.Exists(@path + name + ".rfa"))
                {
                    File.Delete(@path + name + ".rfa");
                }

                familyDoc.SaveAs(@path + name + ".rfa");
                var newFamily = familyDoc.LoadFamily(doc);
                familyDoc.Close();

                File.Delete(@path + name + ".rfa");
                File.Delete(@path + name + ".0001.rfa");

                return newFamily;
            }
            catch (Exception)
            {
                TaskDialog.Show("uBIM", "请先保存该项目后再执行命令！");
                return null;
            }
        }

        public Solid GetSolid(FamilyInstance instance)
        {
            Solid solid = null;
            Options geomOptions = new Options();
            geomOptions.ComputeReferences = true;
            if (instance == null)
            {
                return solid;
            }
            GeometryElement geoElement = instance.get_Geometry(new Options(geomOptions));
            var transForm = instance.GetTransform();
            foreach (var go in geoElement)
            {
                if (go is Solid)
                {
                    solid = go as Solid;
                    if (solid != null && solid.Volume != 0)
                    {
                        break;
                    }
                }
            }

            return solid;

        }

        public FamilyParameter AddParameter(FamilyManager familyManager, string parameterName, ParameterType parameterType, bool isInstance)
        {
            var exitFlag = false;
            var parameters = familyManager.Parameters;
            foreach (FamilyParameter item in parameters)
            {
                if (item.Definition.Name == parameterName)
                {
                    exitFlag = true;
                    break;
                }
            }

            if (!exitFlag)
            {
                var paraGroup = BuiltInParameterGroup.PG_TEXT;
                var parameter = familyManager.AddParameter(parameterName, paraGroup, parameterType, isInstance);
                return parameter;
            }

            return null;
        }
    }



    internal class MassSelectionFilter : ISelectionFilter
    {
        public bool AllowElement(Element elem)
        {
            if (elem == null || elem.Category == null)
            {
                return false;
            }
            return elem.Category.Id.IntegerValue == new ElementId(BuiltInCategory.OST_Mass).IntegerValue;
        }

        public bool AllowReference(Reference reference, XYZ position)
        {
            return true;
        }
    }
}

