#region Namespaces
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Shapes;
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 RevitAddinCreateBeam
{
    [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 beamFamilyName = "コンクリート-長方形梁";//"ワイド フランジ形鋼"
            // 梁タイプ
            string beamTypeName = "300 x 600mm";//"H600x200x11x17"
            // 梁の間隔
            double spanX = 3600;
            // 梁の数
            int beamCount = 10;
            // 最初の梁の位置を設定
            double firstBeamStartX = 0;
            double firstStartBeamY = 0;
            double firstStartBeamZ = 4000;//レベル２の高さに設定。高さを指定する必要あり
            double firstBeamEndX = firstBeamStartX;
            double firstEndBeamY = firstStartBeamY + 14400;
            double firstEndBeamZ = firstStartBeamZ;
            // 梁の始点位置と終点位置
            double startX = firstBeamStartX; startX = UnitUtils.ConvertToInternalUnits(startX, UnitTypeId.Millimeters);
            double startY = firstStartBeamY; startY = UnitUtils.ConvertToInternalUnits(startY, UnitTypeId.Millimeters);
            double startZ = firstStartBeamZ; startZ = UnitUtils.ConvertToInternalUnits(startZ, UnitTypeId.Millimeters);
            XYZ start = new XYZ(startX, startY, startZ);
            double endX = firstBeamEndX; endX = UnitUtils.ConvertToInternalUnits(endX, UnitTypeId.Millimeters);
            double endY = firstEndBeamY; endY = UnitUtils.ConvertToInternalUnits(endY, UnitTypeId.Millimeters);
            double endZ = firstEndBeamZ; endZ = UnitUtils.ConvertToInternalUnits(endZ, UnitTypeId.Millimeters);
            XYZ end = new XYZ(endX, endY, endZ);
            // 梁の直線Lineを作る
            Autodesk.Revit.DB.Line line = Autodesk.Revit.DB.Line.CreateBound(start, end);
            // 複数の梁の直線を格納するリスト
            IList<Curve> lines = new List<Curve>();
            // 梁の指定間隔で指定数だけ梁の直線を作成する
            spanX = UnitUtils.ConvertToInternalUnits(spanX, UnitTypeId.Millimeters);
            for (int i = 0; i < beamCount; i++)
            {
                // 直線をリストに格納する
                lines.Add(line);
                // 指定間隔分だけ移動して次の梁の直線を作成する
                start = start.Add(new(spanX, 0, 0));
                end = end.Add(new(spanX, 0, 0));
                line = Autodesk.Revit.DB.Line.CreateBound(start, end);
            }            
            //現在開いている平面ビューのレベルを取得する
            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;
            }

            //梁ファミリとタイプを検索して取得する
            FamilySymbol beamType = null;
            try
            {
                //プロジェクトに登録されている上記に対応したファミリとタイプ（シンボル）を検索して取得する
                //梁ファミリを検索
                Family beamFamily = new FilteredElementCollector(doc).OfClass(typeof(Family)).FirstOrDefault(q => q.Name == beamFamilyName) as Family;
                if (beamFamily != null)
                {
                    //梁タイプを検索
                    beamType = beamFamily.GetFamilySymbolIds().Select(q => doc.GetElement(q)).First(q => q.Name == beamTypeName) as FamilySymbol;
                }
                else
                {
                    TaskDialog.Show("エラー", $"指定された梁ファミリ '{beamFamilyName}' が見つかりません。");
                    return Result.Failed;
                }
                //梁を配置する
                if (beamType != null && level != null)
                {
                    using (Transaction tx = new Transaction(doc))
                    {
                        tx.Start("Create Beam");
                        //梁の配置
                        //梁タイプが非アクティブならアクティブにする
                        if (!beamType.IsActive) beamType.Activate();
                        //梁を配置
                        foreach (var item in lines)
                        {
                            // 梁配置　levelは指定しても設置高さには影響しない模様。nullでもよい。梁の高さは線分のZ座標で決まる。
                            FamilyInstance beam = doc.Create.NewFamilyInstance(item, beamType, level, StructuralType.Beam);
                        }
                        tx.Commit();
                    }
                }
            }
            catch (Exception ex)
            {
                // 例外が発生した場合のエラーメッセージ
                TaskDialog.Show("エラー", $"梁の作成に失敗しました。エラーメッセージ：{ex.Message}");
                return Result.Failed;
            }
            return Result.Succeeded;
        }
        //ミリメートル単位を内部長さ単位に変換
        private double Uni(double x)
        {
            return UnitUtils.ConvertToInternalUnits(x, UnitTypeId.Millimeters);
        }
    }
}
