#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Structure;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;

#endregion

namespace RevitAddinInstallLightingFixture
{
    [Transaction(TransactionMode.Manual)]
    public class Command : IExternalCommand
    {
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Application app = uiapp.Application;
            Document doc = uidoc.Document;

            //*******条件**********
            //照明器具タイプ
            string lightTypeName = "XND1567SL-LE9";
            
            try
            {
                // 横の照明器具台数
                int xNumber = 0;
                // 縦の照明器具台数
                int yNumber = 0;
                //設置高さ
                double height = 0;
                // Windowを表示してユーザーに横と縦の台数を入力してもらう
                Window1 window1 = new Window1();
                bool? result = window1.ShowDialog();
                // OKボタンが押されたかどうかを確認
                if (result == true)
                {
                    // okButtonが押されたら縦横の台数、高さを取得
                    xNumber = window1.GetXNumber();
                    yNumber = window1.GetYNumber();
                    height = window1.GetHeight();
                    height = UnitUtils.ConvertToInternalUnits(height, UnitTypeId.Millimeters);
                }
                else
                {
                    // キャンセルや閉じるボタンが押された場合の処理
                    return Result.Cancelled;
                }

                // 現在開いている平面ビューのレベルを取得する
                Level level = null;
                View activeView = uidoc.ActiveView;
                //平面ビューか？
                if (activeView is ViewPlan viewPlan)
                {
                    // 平面ビューのレベルを取得
                    level = doc.GetElement(viewPlan.GenLevel.Id) as Level;
                }
                else
                {
                    TaskDialog.Show("エラー", "現在のビューは平面ビューではありません。");
                    return Result.Failed;
                }
                // スペース用選択フィルターを設定
                SpaceSelectionFilter filter = new SpaceSelectionFilter(doc);
                Reference myRef = uidoc.Selection.PickObject(ObjectType.Element, filter, "スペースを選択してください。キャンセルするにはESCを押します");
                // 選択された要素を取得
                Element selectElement = doc.GetElement(myRef);
                //selectElementをスペースとして認識する
                Autodesk.Revit.DB.Mechanical.Space space = selectElement as Autodesk.Revit.DB.Mechanical.Space;
                //スペースのバウンディングボックスを取得
                (XYZ pMin, XYZ pMax) = GetSpaceBoundary(space);
                //スペースの幅と奥行を取得（内部単位のまま）
                double width = pMax.X - pMin.X;
                double depth = pMax.Y - pMin.Y;
                //照明器具の間隔を計算する
                //横方向の間隔
                double dx = width / xNumber;
                //縦方向の間隔
                double dy = depth / yNumber;

                //プロジェクトに登録されている上記に対応した照明タイプ（シンボル）を検索して取得する
                FamilySymbol lightType = new FilteredElementCollector(doc).OfClass(typeof(FamilySymbol))
                    .OfCategory(BuiltInCategory.OST_LightingFixtures).FirstOrDefault(q => q.Name == lightTypeName) as FamilySymbol;

                //照明器具を配置する
                if (lightType != null && level != null && space != null)
                {
                    using (Transaction tx = new Transaction(doc))
                    {
                        tx.Start("Install LightingFixture");
                        //照明器具タイプが非アクティブならアクティブにする
                        if (!lightType.IsActive) lightType.Activate();
                        //配置する
                        for (int i = 0; i < xNumber; i++)
                        {
                            for (int j = 0; j < yNumber; j++)
                            {
                                //配置ポイントを計算（高さは当該階レベルの高さを加えること。Z座標は設計GLからの高さを意味している）
                                XYZ pos = new XYZ(pMin.X + dx/2 + i * dx, pMin.Y + dy/2 + j * dy, height + level.Elevation);
                                //照明器具を配置
                                FamilyInstance lightingFixture = doc.Create.NewFamilyInstance(pos, lightType, level, StructuralType.NonStructural);
                            }
                        }
                        tx.Commit();
                    }
                }
                else
                {
                    TaskDialog.Show("エラー", "照明器具タイプ、レベル、スペースのいずれかがnullです");
                }
            }
            catch (Exception ex)
            {
                // 例外が発生した場合のエラーメッセージ
                TaskDialog.Show("エラー", $"照明器具の配置に失敗しました。エラーメッセージ：{ex.Message}");
                return Result.Failed;
            }
            return Result.Succeeded;
        }
        public (XYZ pMin, XYZ pMax) GetSpaceBoundary(Autodesk.Revit.DB.Mechanical.Space space)
        {
            //spaceのバウンディングボックスを取得
            BoundingBoxXYZ bb = space.get_BoundingBox(null);
            //spaceの最小点と最大点を取得
            XYZ pMin = bb.Min;
            XYZ pMax = bb.Max;
            return (pMin, pMax);
        }
    }
    //スペースだけ選択可能にするフィルター
    public class SpaceSelectionFilter : ISelectionFilter
    {
        private readonly Document doc;
        //コンストラクタ
        public SpaceSelectionFilter(Document doc)
        {
            this.doc = doc;
        }
        //要素の選択を許可するかどうかのメソッド
        public bool AllowElement(Element elem)
        {
            if (elem is Autodesk.Revit.DB.SpatialElement)
            {
                return true;
            }
            return false;
        }
        //要素の面やエッジなどジオメトリへの参照を選択できるかどうかのメソッド
        public bool AllowReference(Reference reference, XYZ position)
        {
            return false;
        }
    }
}
