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

#endregion

namespace RevitAddinInstallFurniture
{
    [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;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Document doc = uidoc.Document;

            //*******条件**********
            //家具ファミリとタイプ名
            string ACFamilyName = "コクヨ_デスク_ワークヴィスタ+_両面_固定式_D1200_基本";
            string ACTypeName = "W3000_天板白_脚白";

            try
            {
                // 横の家具台数
                int xNumber = 9;//机2台ごとに1台分のRoomをあけるので実配置数は6となる
                // 縦の家具台数
                int yNumber = 3;
                //横の配置間隔
                double xInterval = 3000;//mm
                xInterval = UnitUtils.ConvertToInternalUnits(xInterval, UnitTypeId.Millimeters);
                //縦の配置間隔
                double yInterval = 4000;//mm
                yInterval = UnitUtils.ConvertToInternalUnits(yInterval, UnitTypeId.Millimeters);

                // 現在開いている平面ビューのレベルを取得する
                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;
                }
                // Room用選択フィルターを設定
                RoomSelectionFilter filter = new RoomSelectionFilter(doc);
                Reference myRef = uidoc.Selection.PickObject(ObjectType.Element, filter, "Roomを選択してください。キャンセルするにはESCを押します");
                // 選択された要素を取得
                Element selectElement = doc.GetElement(myRef);
                //selectElementをRoomとして認識する
                Autodesk.Revit.DB.Architecture.Room room = selectElement as Autodesk.Revit.DB.Architecture.Room;
                //Roomのバウンディングボックスを取得（家具配置の開始点を決定するため）
                (XYZ pMin, XYZ pMax) = GetRoomBoundary(room);
                
                //プロジェクトに登録されている上記に対応した家具タイプ（シンボル）を検索して取得する
                FamilySymbol furnitureType = new FilteredElementCollector(doc)
                    .OfClass(typeof(FamilySymbol))
                    .OfCategory(BuiltInCategory.OST_Furniture)
                    .Cast<FamilySymbol>()
                    .FirstOrDefault(x => (x.Family.Name == ACFamilyName)
                    && ((x.Name == ACTypeName)));

                //家具を配置する
                if (furnitureType != null && level != null && room != null)
                {
                    using (Transaction tx = new Transaction(doc))
                    {
                        tx.Start("Install FURNITURE");
                        //家具タイプが非アクティブならアクティブにする
                        if (!furnitureType.IsActive) furnitureType.Activate();
                        //配置する
                        for (int i = 0; i < xNumber; i++)
                        {
                            for (int j = 0; j < yNumber; j++)
                            {
                                //配置ポイントを計算
                                XYZ pos = new XYZ(pMin.X + xInterval + i * xInterval, pMin.Y + yInterval + j * yInterval, 0);
                                //家具を配置（３の倍数のときはRoomにするので家具を配置しない）
                                if((i % 3) != 0)
                                {
                                    FamilyInstance lightingFixture = doc.Create.NewFamilyInstance(pos, furnitureType, level, StructuralType.NonStructural);
                                }
                            }
                        }
                        tx.Commit();
                    }
                }
                else
                {
                    TaskDialog.Show("エラー", "家具タイプ、レベル、Roomのいずれかがnullです");
                }
            }
            catch (Exception ex)
            {
                // 例外が発生した場合のエラーメッセージ
                TaskDialog.Show("エラー", $"家具の配置に失敗しました。エラーメッセージ：{ex.Message}");
                return Result.Failed;
            }
            return Result.Succeeded;
        }
        public (XYZ pMin, XYZ pMax) GetRoomBoundary(Autodesk.Revit.DB.Architecture.Room room)
        {
            //spaceのバウンディングボックスを取得
            BoundingBoxXYZ bb = room.get_BoundingBox(null);
            //spaceの最小点と最大点を取得
            XYZ pMin = bb.Min;
            XYZ pMax = bb.Max;
            return (pMin, pMax);
        }
    }
    //Roomだけ選択可能にするフィルター
    public class RoomSelectionFilter : ISelectionFilter
    {
        private readonly Document doc;
        //コンストラクタ
        public RoomSelectionFilter(Document doc)
        {
            this.doc = doc;
        }
        //要素の選択を許可するかどうかのメソッド
        public bool AllowElement(Element elem)
        {
            if (elem is Autodesk.Revit.DB.Architecture.Room)
            {
                return true;
            }
            return false;
        }
        //要素の面やエッジなどジオメトリへの参照を選択できるかどうかのメソッド
        public bool AllowReference(Reference reference, XYZ position)
        {
            return false;
        }
    }
}
