#region Namespaces
using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Xml.Linq;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.DB.Architecture;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using Microsoft.Win32;
using OfficeOpenXml;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical;
using OfficeOpenXml.Style;
using static System.Runtime.InteropServices.JavaScript.JSType;

#endregion

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

            // 表形式のデータを扱うためにDataTableを作成する  
            DataTable datatable = new DataTable("tblData");
            // 列名、型を設定する  
            datatable.Columns.Add("階", typeof(string));
            datatable.Columns.Add("階の地上高", typeof(double));
            datatable.Columns.Add("部屋番号", typeof(int));
            datatable.Columns.Add("部屋名", typeof(string));
            datatable.Columns.Add("部屋面積(m2)", typeof(double));
            datatable.Columns.Add("部屋容積(m3)", typeof(double));

            // フィルタでモデルの中の全ての部屋を抽出  
            FilteredElementCollector col = new FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Rooms);

            //Revitの容積計算設定がONになっているかのフラグ
            bool isVolumeCompEnable = false;

            // DataTableで部屋情報を整理  
            foreach (Room room in col)
            {
                var row = datatable.NewRow();
                string level = room.get_Parameter(BuiltInParameter.ROOM_LEVEL_ID).AsValueString();// 階  
                double levelHeight = room.Level.get_Parameter(BuiltInParameter.LEVEL_ELEV).AsDouble();// 階の地上高
                int roomNumber = int.Parse(room.get_Parameter(BuiltInParameter.ROOM_NUMBER).AsString());// 部屋番号  
                string roomName = room.get_Parameter(BuiltInParameter.ROOM_NAME).AsString();// 室名  
                double roomArea = room.get_Parameter(BuiltInParameter.ROOM_AREA).AsDouble();// 面積  
                roomArea = UnitUtils.ConvertFromInternalUnits(roomArea, UnitTypeId.SquareMeters);
                double roomVolume = room.get_Parameter(BuiltInParameter.ROOM_VOLUME).AsDouble();// 容積  
                roomVolume = UnitUtils.ConvertFromInternalUnits(roomVolume, UnitTypeId.CubicMeters);

                //全部の部屋の容積計算が0だと容積計算がOFFと判断する
                if (roomVolume > 0) isVolumeCompEnable = true;

                row[0] = level;
                row[1] = levelHeight;
                row[2] = roomNumber;
                row[3] = roomName;
                row[4] = roomArea;
                row[5] = roomVolume;
                datatable.Rows.Add(row);
            }
            //容積計算可能かチェック
            if (!isVolumeCompEnable)
            {
                // 容積計算がOFFの場合は警告を出す  
                TaskDialog.Show("処理キャンセル", "容積計算がOFFになっています。処理はキャンセルします。Revitの容積計算設定を確認してください。");
                return Result.Cancelled;
            }

            // ソートする  
            DataView dv = datatable.DefaultView;
            dv.Sort = "階の地上高, 部屋番号";
            DataTable sortedtable = dv.ToTable();

            // 列「階の地上高」をExcelに出力したくない場合には削除
            //sortedtable.Columns.Remove("階の地上高");

            // 次に、室一覧をExcelに保存するためのダイアログを開く  
            SaveFileDialog saveFileDialog = SaveExcel();
            string filename = saveFileDialog.FileName;

            // 保存ファイル名が正常に入力されていればExcel保存実行  
            if (filename != "")
            {
                // ユーザーが指定したExcelファイル名でファイルストリームを取得する  
                FileStream fs = (FileStream)saveFileDialog.OpenFile();
                // ExcelPackageを作成する  
                using (ExcelPackage excel = new ExcelPackage(fs))
                {
                    // ExcelのワークシートにRoomsという名称のシートを作る  
                    ExcelWorksheet worksheet = excel.Workbook.Worksheets.Add("Rooms");
                    // Excelの一番左上の位置A1からDataTableを一気に流し込む  
                    worksheet.Cells["A1"].LoadFromDataTable(sortedtable, true);
                    // Excelの全シートに対して書式を整える  
                    foreach (ExcelWorksheet sheet in excel.Workbook.Worksheets)
                    {
                        // 列幅はオートフィットさせる。最後に実行したほうがよい  
                        for (int i = 1; i < sheet.Dimension.End.Column; i++)
                        {
                            sheet.Column(i).AutoFit();
                            sheet.Column(i).Style.HorizontalAlignment = ExcelHorizontalAlignment.Left;
                        }
                        // 数字の列は右揃えにする  
                        for (int i = 5; i <= 6; i++)
                        {
                            sheet.Column(i).Style.HorizontalAlignment = ExcelHorizontalAlignment.Right;
                        }
                    }
                    // Excel編集の終了　Excelファイルを保存する  
                    excel.Save();

                    // 正常時にメッセージを出す  
                    TaskDialog.Show("正常終了", "Excel出力は正常終了。この後Excelファイルを開きます。");
                    // 確認のためにExcelを起動して保存したファイルを開く  
                    Process process = new Process();
                    process.StartInfo.FileName = filename; // ファイル名のみ指定  
                    process.StartInfo.UseShellExecute = true; // .NET8ではこれが必要 
                    process.Start();
                }
            }
            else
            {
                // ユーザーがExcel出力のダイアログでファイル名を入力してないか、キャンセルした場合は処理をキャンセルする  
                TaskDialog.Show("キャンセル", "キャンセルかファイル名がありません。アドインは終了します。");
                return Result.Cancelled;
            }
            return Result.Succeeded;
        }
        //Excelファイルの保存のためのダイアログ
        public static SaveFileDialog SaveExcel()
        {
            // Excelファイルに保存するためにファイル名をユーザーにきくダイアログを表示する  
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "ExcelFiles | *.xls;*.xlsx;*.xlsm";
            saveFileDialog.Title = "EXCELファイル保存";
            saveFileDialog.ShowDialog();
            // ユーザーがファイル拡張子入れない場合にはxlsになってしまう。あとで開くときに警告が出るのであらかじめxlsxに変更しておく  
            // filenameはユーザーが何も入れずにキャンセルボタンを押すと空になってしまう。  
            string filename = saveFileDialog.FileName;
            // 半角空白が含まれているとExcelアプリ起動時にファイルが見つからないエラーが出る場合あるので半角空白をアンダースコアに置き換える 
            filename = filename.Replace(" ", "_");
            if (filename.IndexOf("xlsx") < 0)
            {
                // SaveFileDialogオブジェクトのFileName属性も変更しておく  
                saveFileDialog.FileName = filename.Split('.')[0] + ".xlsx";
            }
            return saveFileDialog;
        }
    }
}
