#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.UI;
using Autodesk.Revit.UI.Selection;

#endregion

namespace RevitAddinChangeWallProps
{
    [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;

            // 選択フィルターを設定
            WallSelectionFilter filter = new WallSelectionFilter(doc);

            try
            {
                // フィルター付きでユーザーに壁インスタンスを選択してもらう
                Reference myRef = uidoc.Selection.PickObject(ObjectType.Element, filter, "壁を選択してください。キャンセルするにはESCを押します");

                // 選択された要素を取得
                Element selectElement = doc.GetElement(myRef);
                // Wallクラスのインスタンスにキャスト
                Wall wall = selectElement as Wall;

                // 壁が上部にアタッチされているか判定
                Parameter isAttachedParam = wall.get_Parameter(BuiltInParameter.WALL_TOP_IS_ATTACHED);
                if (isAttachedParam.AsInteger() == 1)
                {
                    //上部にアタッチされている場合は、警告を出して終了する
                    // ユーザーに上部アタッチを解除してもらう
                    TaskDialog.Show("警告", "この壁は上部にアタッチされていて高さが変更できません。上部アタッチを手動解除してください。");
                    return Result.Cancelled;
                }

                //現在の壁の高さを取得
                Parameter heightParam = wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM);
                double currentHeight = heightParam.AsDouble();
                //内部単位をミリメートルに変換
                double currentHeightMM = (int)Math.Round(UnitUtils.ConvertFromInternalUnits(currentHeight, UnitTypeId.Millimeters), 0);
                // 変更したい壁の上部オフセット値
                double topOffsetMM = -1000;// 1000mm下げる
                //内部単位に変換
                double topOffset = UnitUtils.ConvertToInternalUnits(topOffsetMM, UnitTypeId.Millimeters);
                //新しい壁の高さを内部単位で計算する
                double newHeight = currentHeight + topOffset;

                using (Transaction tx = new Transaction(doc, "Change Wall Height"))
                {
                    tx.Start();
                    //壁の「上部レベル」パラメータを取得して確認する
                    Parameter pm = wall.get_Parameter(BuiltInParameter.WALL_HEIGHT_TYPE);
                    //「上部レベル」のパラメータが上部の階ではなく「指定」（値はInvalidElementId）になっている場合
                    if (pm.AsElementId() == ElementId.InvalidElementId)
                    {
                        //「指定高さ」パラメータを変更する
                        wall.get_Parameter(BuiltInParameter.WALL_USER_HEIGHT_PARAM).Set(newHeight);
                    }
                    else
                    {
                        //「上部レベル」のパラメータがいずれか上部の階が指定されている場合
                        //「上部オフセット」パラメータを変更する
                        wall.get_Parameter(BuiltInParameter.WALL_TOP_OFFSET).Set(topOffset);
                    }
                    tx.Commit();
                }
                TaskDialog.Show("Error", $"高さを{currentHeightMM}mmから{topOffsetMM}mmだけ変更しました");
            }
            catch (Autodesk.Revit.Exceptions.OperationCanceledException)
            {
                // ユーザーがキャンセルした場合
                return Result.Cancelled;
            }
            catch (Exception ex)
            {
                // その他のエラーが発生した場合
                message = $"エラーが発生しました: {ex.Message}";
                return Result.Failed;
            }
            return Result.Succeeded;
        }
    }
    public class WallSelectionFilter : ISelectionFilter
    {
        private readonly Document doc;
        //コンストラクタ
        public WallSelectionFilter(Document doc)
        {
            this.doc = doc;
        }
        //要素の選択を許可するかどうかのメソッド
        public bool AllowElement(Element elem)
        {
            if (elem is Autodesk.Revit.DB.Wall)
            {
                return true;
            }
            return false;
        }
        //要素の面やエッジなどジオメトリへの参照を選択できるかどうかのメソッド
        public bool AllowReference(Reference reference, XYZ position)
        {
            return false;
        }
    }
}
