using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Xml;

using Excel = Microsoft.Office.Interop.Excel;

namespace CommonMP.HYMCO.OptionImpl.McStructureXmlFileEditorForDevelop
{
    /// <summary><para>class outline:</para>
    /// <para>構造定義ファイル作成用クラス</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2010/11/01][新規作成]</para>
    /// </remarks>
    public class StructFileCreater : StructFileController
    {
        /// <summary> Excelシート名：モデル情報 </summary>
        static readonly string SHEETNAME_MODEL_MST = Properties.McStructureXmlFileEditorResources.MODEL_INFORMATION;
        /// <summary> Excelシート名：接続情報 </summary>
        static readonly string SHEETNAME_CONN_MST = Properties.McStructureXmlFileEditorResources.CONNECTION_INFORMATION;

        /// <summary> Excelシート名：①モデル配置 </summary>
        static readonly string SHEETNAME_1AREA = Properties.McStructureXmlFileEditorResources.SHEETNAME_MODEL_PLACEMENT;
        /// <summary> Excelシート名：②要素モデル </summary>
        static readonly string SHEETNAME_2MODEL = Properties.McStructureXmlFileEditorResources.SHEETNAME_ELEMENT_MODEL;
        /// <summary> Excelシート名：③要素接続 </summary>
        static readonly string SHEETNAME_3CONN = Properties.McStructureXmlFileEditorResources.SHEETNAME_ELEMENT_CONNECTION;

        /// <summary>モデル情報マスタ列名リスト(※ModelKeyには[Model|FactoryID]を格納) </summary>
        private string m_sModelMstColumnsLst = "iRow,ModelKey,"
                                             + "ModelName,Model,FactoryID,ShapeKind,InOut,InterpolateType,PatternID,Pattern,Connection";
        /// <summary>接続情報マスタ列名リスト </summary>
        private string m_sConnMstColumnsLst = "Name,TranInfo,FactoryID,VariKey0,VariName0,VariKey1,VariName1,VariKey2,VariName2,VariKey3,VariName3,VariKey4,VariName4";

        /// <summary>モデル情報マスタ </summary>
        private DataTable m_dtModelMst = null;
        /// <summary>接続情報マスタ </summary>
        private DataTable m_dtConnMst = null;

        /// <summary>1セルの幅 </summary>
        private double m_dCelWidth = 70;
        /// <summary>1セルの高さ </summary>
        private double m_dCelHeight = 30;

        /// <summary> Excelヘッダー行数=2行（①モデル配置）</summary>
        private const int AREASHEET_HEADERROW = 2;
        /// <summary> Excelヘッダー行数=3行（②要素モデル/③要素接続）</summary>
        private const int EDITSHEET_HEADERROW = 3;

        /// <summary>Excelテンプレートファイルパス </summary>
        private string m_sTemplatePath = null;
        /// <summary>Excelテンプレートファイル名 </summary>
        private string m_sTemplateFile = null;

        

        /// <summary><para>method outline:</para>
        /// <para>コンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>StructFileCreater csStruct = new StructFileCreater(sTemplatePath, sTemplateFileName);</para>
        /// </example>
        /// <param name="sTemplatePath">Excelテンプレートファイルパス</param>
        /// <param name="sTemplateFileName">Excelテンプレートファイル名</param>
        /// <returns>StructFileCreater 生成されたインスタンス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public StructFileCreater(string sTemplatePath, string sTemplateFileName)
        {
            m_sTemplatePath = sTemplatePath;
            m_sTemplateFile = sTemplateFileName;

            //Excelテンプレートからモデル情報を読み込む
            ReadModelMst();
        }


        #region Excelテンプレートからモデル情報読み込み

        /// <summary><para>method outline:</para>
        /// <para>Excelテンプレートからモデル情報読み込み</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ReadModelMst();</para>
        /// </example>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">Excelテンプレートファイル読込みに失敗</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private void ReadModelMst()
        {
            ExcelController csExcel = new ExcelController();
            try
            {
                //--------------------------------------------------
                // テーブル初期化
                //--------------------------------------------------
                string[] sCols = m_sModelMstColumnsLst.Split(',');
                m_dtModelMst = new DataTable();
                for (int i = 0; i < sCols.Length; i++)
                {
                    string sType = "System.String";
                    if (sCols[i].StartsWith("i"))
                        sType = "System.Int32";
                    m_dtModelMst.Columns.Add(sCols[i], Type.GetType(sType));
                }

                sCols = m_sConnMstColumnsLst.Split(',');
                m_dtConnMst = new DataTable();
                for (int i = 0; i < sCols.Length; i++)
                {
                    string sType = "System.String";
                    if (sCols[i].StartsWith("i"))
                        sType = "System.Int32";
                    m_dtConnMst.Columns.Add(sCols[i], Type.GetType(sType));
                }

                //--------------------------------------------------
                // Excelテンプレートからモデル情報を読み込む
                //--------------------------------------------------
                //Excelﾃﾝﾌﾟﾚｰﾄﾌｧｲﾙｵｰﾌﾟﾝ
                csExcel.OpenBook(m_sTemplatePath + "\\" + m_sTemplateFile);

                //シート切り替え
                csExcel.SetSheet(SHEETNAME_MODEL_MST);

                int iRow = Convert.ToInt32(csExcel.GetCell(1, 1));  //データ数取得
                object[,] oVals = csExcel.GetRange(string.Format("B{0}", 3), string.Format("J{0}", iRow + 2));

                int iSetStartCol = 2;
                for (int j = 1; j <= iRow; j++)
                {
                    DataRow dr = m_dtModelMst.NewRow();
                    for (int i = 1; i <= (m_dtModelMst.Columns.Count - iSetStartCol); i++)
                    {
                        string sVal = Convert.ToString(oVals[j, i]);
                        if (i <= 4 && sVal.Length == 0 && m_dtModelMst.Rows.Count > 0)
                        {   //モデル名称、モデル種別、モデルファクトリ識別子、ShapeKindはﾌﾞﾗﾝｸの場合は前行と同じ値を埋める
                            sVal = (string)m_dtModelMst.Rows[m_dtModelMst.Rows.Count - 1][iSetStartCol + i - 1];
                        }
                        dr[iSetStartCol + i - 1] = sVal;
                    }
                    dr["iRow"] = j;
                    dr["ModelKey"] = string.Format(StructFileController.MODEL_KEY_FORMAT, dr["Model"], dr["FactoryID"]);

                    if (!string.IsNullOrEmpty((string)dr["InOut"]))
                        m_dtModelMst.Rows.Add(dr);
                }


                //シート切り替え
                csExcel.SetSheet(SHEETNAME_CONN_MST);
                oVals = csExcel.GetRange(string.Format("A{0}", 3), string.Format("M{0}", 50));
                for (int j = 1; j <= oVals.GetUpperBound(0); j++)
                {
                    DataRow dr = m_dtConnMst.NewRow();
                    for (int i = 1; i <= (m_dtConnMst.Columns.Count); i++)
                    {
                        dr[i - 1] = Convert.ToString(oVals[j, i]);
                    }
                    if (string.IsNullOrEmpty((string)dr[0]))
                        break;

                    m_dtConnMst.Rows.Add(dr);
                }

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
                //テンプレートファイル読込みでエラーが発生しました。
                throw new Exception(Properties.McStructureXmlFileEditorResources.MSG_ERROR_TEMPLATE_FILE_READ + "\n\n" + ex.Message);
            }
            finally
            {
                //Excelｱﾌﾟﾘｹｰｼｮﾝ終了
                csExcel.AppQuit();

                //COM オブジェクト解放
                csExcel.Dispose();
            }
        }

        #endregion

        #region モデル情報マスタ・接続情報マスタから情報取得

        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタ・接続情報マスタからモデル名リスト・要素接続リスト取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>GetMstList(oListModel, sListConn);</para>
        /// </example>
        /// <param name="oListModel">モデル名リスト</param>
        /// <param name="sListConn">要素接続リスト</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private void GetMstList(out object[,] oListModel, out string sListConn)
        {
            //モデル名称のリストを作成
            DataTable dt = m_dtModelMst.DefaultView.ToTable(true, "ModelName");
            oListModel = new string[dt.Rows.Count, 1];
            for (int i = 0; i < dt.Rows.Count; i++ )
            {
                oListModel[i, 0] = (string)dt.Rows[i]["ModelName"];
            }

            //要素接続のリストを作成
            string[] sConnections = new string[m_dtConnMst.Rows.Count];
            for (int i = 0; i < m_dtConnMst.Rows.Count; i++)
            {
                sConnections[i] = (string)m_dtConnMst.Rows[i]["Name"];
            }
            sListConn = string.Join(",", sConnections);
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタからモデル名取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string sModelName = GetModelName(sModel, sFactoryID);</para>
        /// </example>
        /// <param name="sModel">モデル種別</param>
        /// <param name="sFactoryID">モデルファクトリ識別子</param>
        /// <returns>モデル名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetModelName(string sModel, string sFactoryID)
        {
            string sModelKey = string.Format(StructFileController.MODEL_KEY_FORMAT, sModel, sFactoryID);
            string sName = sModel;
            DataRow[] drMsts = m_dtModelMst.Select(string.Format("ModelKey='{0}'", sModelKey));
            if (drMsts.Length > 0)
            {
                sName = drMsts[0]["ModelName"].ToString();
            }
            return sName;
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタからモデル情報取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = GetModelInfo(item);</para>
        /// </example>
        /// <param name="item">格納用CalElementItem</param>
        /// <returns>true=マスタあり/false=なし</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private bool GetModelInfo(CalElementItem item)
        {
            DataRow[] drMsts = m_dtModelMst.Select(string.Format("ModelName='{0}'", item.ModelName));
            if (drMsts.Length > 0)
            {
                item.Model = drMsts[0]["Model"].ToString();
                item.FactoryID = drMsts[0]["FactoryID"].ToString();
                item.ShapeKind = drMsts[0]["ShapeKind"].ToString();

                if (item.ShapeKind == "Basin")
                {
                    item.Width = 20;
                    item.Height = 20;
                }
                else
                {
                    item.Width = 40;
                    item.Height = 20;
                }

                return true;
            }
            return false;
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタからパターン名取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string sPatternName = GetModelPattern(sModel, sFactoryID, sInOut, sPatternID);</para>
        /// </example>
        /// <param name="sModel">モデル種別</param>
        /// <param name="sFactoryID">モデルファクトリ識別子</param>
        /// <param name="sInOut">"受信" or "送信"</param>
        /// <param name="sPatternID">パターンID</param>
        /// <returns>パターン名</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetModelPattern(string sModel, string sFactoryID, string sInOut, string sPatternID)
        {
            string sModelKey = string.Format(StructFileController.MODEL_KEY_FORMAT, sModel, sFactoryID);
            string sName = sPatternID;
            DataRow[] drMsts = m_dtModelMst.Select(string.Format("ModelKey='{0}' AND InOut='{1}' AND PatternID='{2}'", 
                                                                    sModelKey, sInOut, sPatternID));
            if (drMsts.Length > 0)
            {
                sName = drMsts[0]["Pattern"].ToString();
            }
            return sName;
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタからパターンID取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string sPatternID = GetModelPatternID(sModel, sFactoryID, sInOut, sPattern, sConnectionName, ref sInterpolateType);</para>
        /// </example>
        /// <param name="sModel">モデル種別</param>
        /// <param name="sFactoryID">モデルファクトリ識別子</param>
        /// <param name="sInOut">"受信" or "送信"</param>
        /// <param name="sPattern">パターン名</param>
        /// <param name="sConnectionName">接続線名(ex.１次元配列時系列情報)</param>
        /// <param name="sInterpolateType">[out]InterpolateType（※受信の場合のみ）</param>
        /// <returns>パターンID</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetModelPatternID(string sModel, string sFactoryID, string sInOut, string sPattern, string sConnectionName, ref string sInterpolateType)
        {
            string sModelKey = string.Format(StructFileController.MODEL_KEY_FORMAT, sModel, sFactoryID);
            string sID = "";
            DataRow[] drMsts = m_dtModelMst.Select(string.Format("ModelKey='{0}' AND InOut='{1}' AND Pattern='{2}' AND Connection = '{3}'",
                                                            sModelKey, sInOut, sPattern, sConnectionName));
            if (drMsts.Length > 0)
            {
                sID = drMsts[0]["PatternID"].ToString();
                sInterpolateType = drMsts[0]["InterpolateType"].ToString();
            }
            return sID;
        }
        /// <summary><para>method outline:</para>
        /// <para>モデル情報マスタからパターンID取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>string sPatternID = GetModelPatternID(sModel, sFactoryID, sInOut, sPattern, sConnectionName);</para>
        /// </example>
        /// <param name="sModel">モデル種別</param>
        /// <param name="sFactoryID">モデルファクトリ識別子</param>
        /// <param name="sInOut">"受信" or "送信"</param>
        /// <param name="sPattern">パターン名</param>
        /// <param name="sConnectionName">接続線名(ex.１次元配列時系列情報)</param>
        /// <returns>パターンID</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private string GetModelPatternID(string sModel, string sFactoryID, string sInOut, string sPattern, string sConnectionName)
        {
            string sInterpolateType = "";
            return GetModelPatternID(sModel, sFactoryID, sInOut, sPattern, sConnectionName, ref sInterpolateType);
        }

        /// <summary><para>method outline:</para>
        /// <para>接続情報マスタから接続情報取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = GetConnMst(citem, bSearchFromKey);</para>
        /// </example>
        /// <param name="citem">格納用ConnectionItem</param>
        /// <param name="bSearchFromKey">true=キーから検索/false=名称から検索</param>
        /// <returns>true=マスタあり/false=なし</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private bool GetConnMst(ConnectionItem citem, bool bSearchFromKey)
        {
            for (int i = citem.Variables.Count; i <= 4; i++)
            {   //パラメータ変数領域を0～4用意
                CalElementVariable param = new CalElementVariable();
                citem.Variables.Add(param);
            }

            string sFilter = null;
            if (bSearchFromKey)
                sFilter = string.Format("TranInfo='{0}' AND FactoryID='{1}'", citem.TranInfo, citem.FactoryID);
            else
                sFilter = string.Format("Name='{0}'", citem.ConnectionName);

            DataRow[] drMsts = m_dtConnMst.Select(sFilter);
            if (drMsts.Length > 0)
            {
                DataRow dr = drMsts[0];
                if (bSearchFromKey)
                {   //キーから検索した場合、名称をセット
                    citem.ConnectionName = (string)dr["Name"];
                }
                else
                {   //名称から検索した場合、キーをセット
                    citem.TranInfo = (string)dr["TranInfo"];
                    citem.FactoryID = (string)dr["FactoryID"];
                }

                for (int i = 0; i <= 4; i++)
                {
                    CalElementVariable param = citem.Variables[i];
                    param.Key = (string)dr[string.Format("VariKey{0}", i)];
                    param.Name = (string)dr[string.Format("VariName{0}", i)];
                    if (i > 0 && !string.IsNullOrEmpty(param.Key))
                    {
                        param.DataType = "LONG";
                        param.Value = (i < 4 ? "64" : "96");    //デフォルト値
                    }
                }

                return true;
            }
            return false;
        }

        #endregion


        /// <summary><para>method outline:</para>
        /// <para>モデル接続作成用Excel出力</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>OutputExcel(sXmlFileNamePath, sExcelFileNamePath);</para>
        /// </example>
        /// <param name="sXmlFileNamePath">読込む構造定義ファイル名パス（未指定の場合、新規作成用）</param>
        /// <param name="sExcelFileNamePath">出力Excelファイル名パス</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">Excelファイル作成に失敗</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override void OutputExcel(string sXmlFileNamePath, string sExcelFileNamePath)
        {
            ExcelController csExcel = new ExcelController();
            try
            {
                //構造定義ファイル読込み
                LoadXml(sXmlFileNamePath);
                //モデル配置決定
                PositioningCalElement(m_dCelWidth, m_dCelHeight);
                

                //①モデル配置シート用 領域確保
                object[,] oArea = new object[256, 256];
                //②要素モデルシート用 領域確保
                object[,] oModel = new object[m_ndlCalElement.Count, 4];
                //③要素接続シート用 領域確保
                object[,] oConn = new object[m_ndlConnection.Count, 11];

                int iR = 0;
                int iC = 0;
                foreach (DataRow dr in m_dtCalElement.Rows)
                {
                    int iX = (int)dr["CellX"];
                    int iY = (int)dr["CellY"];
                    if (iY < oArea.GetLength(0) && iX < oArea.GetLength(1))
                    {
                        oArea[iY, iX] = dr["No"];
                    }
                    else
                    {   //表示エリアを越えている
                    }

                    iC = 0;
                    oModel[iR, iC++] = dr["No"];
                    oModel[iR, iC++] = GetModelName((string)dr["Model"], (string)dr["FactoryID"]);
                    oModel[iR, iC++] = dr["ElementName"];
                    oModel[iR, iC++] = dr["ID"];
                    iR++;
                }
                iR = 0;
                foreach (XmlElement elmConn in m_ndlConnection)
                {
                    ConnectionItem citem = new ConnectionItem();
                    GetConnectionAttribute(elmConn, citem);
                    
                    //接続情報マスタ取得
                    if (GetConnMst(citem, true))
                    {
                        foreach (CalElementVariable csParam in citem.Variables)
                        {   //プロパティ取得
                            if(!string.IsNullOrEmpty(csParam.Key))
                                csParam.Value = GetConnectionVariable(elmConn, csParam.Key);
                        }
                    }

                    //上流モデル(From)PortのIDから要素Noを検索
                    DataRow dr = SearchPortInCalElement(StructFileController.XML_TAG_SENDPORT, citem.From);
                    if (dr != null)
                    {
                        //要素No
                        citem.FromNo = (int)dr["No"];

                        //[SendElementOutPattern]のPatternID取得
                        citem.FromPatternID = GetPatternID(elmConn, StructFileController.XML_TAG_SEND_OUT_PATTERN);
                        //PatternIDからパターン名を検索(送信)
                        citem.FromPattern = GetModelPattern((string)dr["Model"], (string)dr["FactoryID"],
                            Properties.McStructureXmlFileEditorResources.SEND, citem.FromPatternID);
                    }

                    //下流モデル(To)PortのIDから要素Noを検索
                    dr = SearchPortInCalElement(StructFileController.XML_TAG_RECEIVEPORT, citem.To);
                    if (dr != null)
                    {
                        //要素No
                        citem.ToNo = (int)dr["No"];

                        //[ReceiveElementInPattern]のPatternID取得
                        citem.ToPatternID = GetPatternID(elmConn, StructFileController.XML_TAG_RECEIVE_IN_PATTERN);
                        //PatternIDからパターン名を検索(受信)
                        citem.ToPattern = GetModelPattern((string)dr["Model"], (string)dr["FactoryID"],
                            Properties.McStructureXmlFileEditorResources.RECEIVE, citem.ToPatternID);
                    }

                    iC = 0;
                    oConn[iR, iC++] = citem.ConnectionName;
                    oConn[iR, iC++] = citem.FromNo;
                    oConn[iR, iC++] = citem.FromPattern;
                    oConn[iR, iC++] = citem.ToNo;
                    oConn[iR, iC++] = citem.ToPattern;
                    oConn[iR, iC++] = citem.Variables[1].Value;
                    oConn[iR, iC++] = citem.Variables[2].Value;
                    oConn[iR, iC++] = citem.Variables[3].Value;
                    oConn[iR, iC++] = citem.Variables[4].Value;
                    oConn[iR, iC++] = citem.Name;
                    oConn[iR, iC++] = citem.ID;
                    iR++;
                }

                //モデル名称・要素接続のリスト取得
                object[,] oListModel;
                string sListConn;
                GetMstList(out oListModel, out sListConn);

                //================================================================================
                //Excelﾃﾝﾌﾟﾚｰﾄﾌｧｲﾙｵｰﾌﾟﾝ
                csExcel.OpenBook(m_sTemplatePath + "\\" + m_sTemplateFile);

                //接続情報シートは削除
                csExcel.DeleteSheet(SHEETNAME_CONN_MST);

                
                //①モデル配置 設定
                csExcel.SetSheet(SHEETNAME_1AREA);
                csExcel.SetCell(AREASHEET_HEADERROW + 1, 1, oArea);
                
                //②要素モデル 設定
                csExcel.SetSheet(SHEETNAME_2MODEL);
                csExcel.SetCell(EDITSHEET_HEADERROW + 1, 1, oModel);
                //入力規則・リスト設定(②のモデル名称)
                csExcel.SetCell(EDITSHEET_HEADERROW + 505, 2, oListModel);
                string sListModel = string.Format("=$B${0}:$B${1}", EDITSHEET_HEADERROW + 505, EDITSHEET_HEADERROW + 505 + oListModel.GetUpperBound(0));
                csExcel.SetValidationList(string.Format("B{0}", EDITSHEET_HEADERROW + 1), string.Format("B{0}", EDITSHEET_HEADERROW + 500), sListModel);

                //③要素接続 設定
                csExcel.SetSheet(SHEETNAME_3CONN);
                csExcel.SetCell(EDITSHEET_HEADERROW + 1, 1, oConn);
                //入力規則・リスト設定(③の接続線)
                csExcel.SetValidationList(string.Format("A{0}", EDITSHEET_HEADERROW + 1), string.Format("A{0}", EDITSHEET_HEADERROW + 500), sListConn);


                //シート保護
                csExcel.SheetProtect(SHEETNAME_MODEL_MST, "", false, false);
                csExcel.SheetProtect(SHEETNAME_1AREA, "", false, false);
                csExcel.SheetProtect(SHEETNAME_2MODEL, "", true, false);
                csExcel.SheetProtect(SHEETNAME_3CONN, "", true, false);
                //ブックを保護設定
                csExcel.BookProtect("");

                //保存して表示
                csExcel.SaveBook(sExcelFileNamePath, false);
                csExcel.xlApp.Visible = true;

                //マクロ実行
                csExcel.MacroRun("OnCreateNewFile", 0);

            }
            catch (Exception ex)
            {
                //Excelｱﾌﾟﾘｹｰｼｮﾝ終了
                csExcel.AppQuit();

                System.Diagnostics.Debug.WriteLine(ex);
                //編集用Excelファイル作成でエラーが発生しました。
                throw new Exception(Properties.McStructureXmlFileEditorResources.MSG_ERROR_CREATE_EXCEL_FOR_EDIT + "\n\n" + ex.Message);
            }
            finally
            {
                //COM オブジェクト解放
                csExcel.Dispose();
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>構造定義ファイル出力</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bChk = OutputStructXml(sXmlFileNamePath, sExcelFileNamePath, sOutputXmlFileNamePath);</para>
        /// </example>
        /// <param name="sXmlFileNamePath">読込む構造定義ファイル名パス（未指定の場合、新規作成用）</param>
        /// <param name="sExcelFileNamePath">読込むExcelファイル名パス</param>
        /// <param name="sOutputXmlFileNamePath">出力構造定義ファイル名パス</param>
        /// <returns>true=正常/false=チェックエラーあり</returns>
        /// <exception cref="System.Exception">構造定義ファイル作成に失敗</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool OutputStructXml(string sXmlFileNamePath, string sExcelFileNamePath, string sOutputXmlFileNamePath)
        {
            ExcelController csExcel = new ExcelController();
            try
            {
                m_csErrorList.Clear();

                //構造定義ファイル読込み
                LoadXml(sXmlFileNamePath);
                //モデル配置決定
                PositioningCalElement(m_dCelWidth, m_dCelHeight);

                
                //================================================================================
                //Excelﾌｧｲﾙｵｰﾌﾟﾝ
                csExcel.OpenBook(sExcelFileNamePath);

                //①モデル配置
                csExcel.SetSheet(SHEETNAME_1AREA);
                //データ取得（※LowerBound=1で格納されている）
                SortedList<int, int[]> lstArea = GetArea(csExcel, 256, 256);

                //②要素モデル
                csExcel.SetSheet(SHEETNAME_2MODEL);
                int iRowCount2 = Convert.ToInt32(csExcel.GetCell(1, 5));    //入力行数:E1
                //データ取得（※LowerBound=1で格納されている）
                object[,] oModel = csExcel.GetRange(EDITSHEET_HEADERROW + 1, 1, iRowCount2, 4);

                //③要素接続
                csExcel.SetSheet(SHEETNAME_3CONN);
                int iRowCount3 = Convert.ToInt32(csExcel.GetCell(1, 9));    //入力行数:I1
                //データ取得（※LowerBound=1で格納されている）
                object[,] oConn = csExcel.GetRange(EDITSHEET_HEADERROW + 1, 1, iRowCount3, 11);

                //================================================================================
                //更新用の構造定義ファイルXML
                XmlDocument xmlDoc = (XmlDocument)m_xmlDoc.Clone();
                List<XmlElement> lstNewCal = new List<XmlElement>();
                List<XmlElement> lstNewCon = new List<XmlElement>();

                //要素モデルを格納
                SortedList<int, CalElementItem> lstCalElement = new SortedList<int, CalElementItem>();
                for (int iR = 1; iR <= oModel.GetUpperBound(0); iR++)
                {
                    CalElementItem eitem = new CalElementItem();
                    int iC = 1;
                    int iNo = Convert.ToInt32(oModel[iR, iC++]);            //要素No
                    eitem.ModelName = Convert.ToString(oModel[iR, iC++]);   //要素モデル
                    eitem.ElementName = Convert.ToString(oModel[iR, iC++]); //名称
                    eitem.ID = Convert.ToString(oModel[iR, iC++]);          //ID

                    if (m_ndlCalElement.Count == 0 || string.IsNullOrEmpty(eitem.ID))
                    {   //ID採番
                        eitem.ID = NewID("E");
                    }

                    //モデル種別、モデルファクトリ識別子取得
                    if (!GetModelInfo(eitem) && !string.IsNullOrEmpty(eitem.ModelName))
                    {   //マスタにないエラー
                        //入力された要素モデル[ {0} ]はマスタに登録されていません。
                        AddError(SHEETNAME_2MODEL, iR + EDITSHEET_HEADERROW, 2, Properties.McStructureXmlFileEditorResources.ELEMENT_MODEL,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_ELEMENT_NOT_REGISTERED_MASTER, eitem.ModelName));
                        continue;
                    }

                    //位置セット
                    if (lstArea.ContainsKey(iNo))
                    {
                        eitem.CellX = lstArea[iNo][0];
                        eitem.CellY = lstArea[iNo][1];
                        eitem.X = Convert.ToInt32(m_iCalElementMinX + eitem.CellX * m_dCelWidth);
                        eitem.Y = Convert.ToInt32(m_iCalElementMinY + eitem.CellY * m_dCelHeight);
                    }
                    else
                    {   //配置がないエラー
                        //入力された要素No[ {0} ]は配置が指定されていません。
                        AddError(SHEETNAME_2MODEL, iR + EDITSHEET_HEADERROW, 1, Properties.McStructureXmlFileEditorResources.ELEMENT_NO, 
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_ELEMENT_NO_NOT_SPECIFIED, iNo));
                        continue;
                    }

                    //配置位置の変更がなければ、もとの位置ｻｲｽﾞをセット
                    SetCalElementPosition(eitem);

                    if (lstCalElement.ContainsKey(iNo))
                    {   //同じ要素Noが入力されているエラー
                        //同じ要素Noが入力されています。
                        AddError(SHEETNAME_2MODEL, iR + EDITSHEET_HEADERROW, 1, Properties.McStructureXmlFileEditorResources.ELEMENT_NO,
                            Properties.McStructureXmlFileEditorResources.MSG_ERROR_SAME_ELEMENT_NO_INPUTTED);
                        continue;
                    }

                    lstCalElement.Add(iNo, eitem);
                }

                //接続を格納
                for (int iR = 1; iR <= oConn.GetUpperBound(0); iR++)
                {
                    ConnectionItem citem = new ConnectionItem();
                    int iC = 1;
                    citem.ConnectionName = Convert.ToString(oConn[iR, iC++]);   //接続線
                    
                    //接続情報マスタ取得
                    if (!GetConnMst(citem, false) && !string.IsNullOrEmpty(citem.ConnectionName))
                    {   //接続マスタにないエラー（未入力はOKとする）
                        //入力された接続線[ {0} ]は存在しません。
                        AddError(SHEETNAME_3CONN, iR + EDITSHEET_HEADERROW, 1, Properties.McStructureXmlFileEditorResources.CONNECTING_LINE,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_CONNECTING_LINE_NOT_EXIST, citem.ConnectionName));
                    }

                    citem.FromNo = Convert.ToInt32(oConn[iR, iC++]);                //From 要素No
                    citem.FromPattern = Convert.ToString(oConn[iR, iC++]);          //From パターン
                    citem.ToNo = Convert.ToInt32(oConn[iR, iC++]);                  //To   要素No
                    citem.ToPattern = Convert.ToString(oConn[iR, iC++]);            //To   パターン
                    for (int i = 1; i <= 4; i++)
                    {   //パラメータ（セル配列数１次元,２次元,３次元,最大レコード数）
                        CalElementVariable param = citem.Variables[i];
                        string sValue = Convert.ToString(oConn[iR, iC]);
                        if (!string.IsNullOrEmpty(sValue))
                            param.Value = sValue;
                        iC++;
                    }
                    citem.Name = Convert.ToString(oConn[iR, iC++]);                 //名称
                    citem.ID = Convert.ToString(oConn[iR, iC++]);                   //ID

                    //From(SendPort)の要素を検索
                    CalElementItem eitemFrom = null;
                    if (lstCalElement.ContainsKey(citem.FromNo))
                    {   //パターンID取得(送信)
                        eitemFrom = lstCalElement[citem.FromNo];
                        citem.FromPatternID = GetModelPatternID(eitemFrom.Model, eitemFrom.FactoryID, Properties.McStructureXmlFileEditorResources.SEND, citem.FromPattern, citem.ConnectionName);
                        if (string.IsNullOrEmpty(citem.FromPatternID) && !string.IsNullOrEmpty(citem.FromPattern))
                        {   //上流モデルパターンエラー
                            //入力されたパターン[ {0} ]はマスタに登録されていません。
                            AddError(SHEETNAME_3CONN, iR + EDITSHEET_HEADERROW, 3, Properties.McStructureXmlFileEditorResources.UPSTREAM_MODEL_PATTERN,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_PATTERN_NOT_REGISTERED_MASTER, citem.FromPattern));
                        }
                    }
                    else
                    {   //上流モデル要素Noエラー
                        //入力された要素No[ {0} ]は要素モデル、または配置が指定されていません。
                        AddError(SHEETNAME_3CONN, iR + EDITSHEET_HEADERROW, 2, Properties.McStructureXmlFileEditorResources.UPSTREAM_MODEL_ELEMENT_NO,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_ELEMENT_NO_NOT_SPECIFIED_MODEL_OR_PLACEMENT, citem.FromNo));
                    }

                    //To(ReceivePort)の要素を検索
                    CalElementItem eitemTo = null;
                    if (lstCalElement.ContainsKey(citem.ToNo))
                    {   //パターンID取得(受信)
                        eitemTo = lstCalElement[citem.ToNo];
                        citem.ToPatternID = GetModelPatternID(eitemTo.Model, eitemTo.FactoryID, Properties.McStructureXmlFileEditorResources.RECEIVE,
                            citem.ToPattern, citem.ConnectionName, ref citem.InterpolateType);
                        if (string.IsNullOrEmpty(citem.ToPatternID) && !string.IsNullOrEmpty(citem.ToPattern))
                        {   //下流モデルパターンエラー
                            //入力されたパターン[ {0} ]はマスタに登録されていません。
                            AddError(SHEETNAME_3CONN, iR + EDITSHEET_HEADERROW, 5, Properties.McStructureXmlFileEditorResources.DOWNSTREAM_MODEL_PATTERN,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_PATTERN_NOT_REGISTERED_MASTER, citem.ToPattern));
                        }
                    }
                    else
                    {   //下流モデル要素Noエラー
                        //入力された要素No[ {0} ]は要素モデル、または配置が指定されていません。
                        AddError(SHEETNAME_3CONN, iR + EDITSHEET_HEADERROW, 4, Properties.McStructureXmlFileEditorResources.DOWNSTREAM_MODEL_ELEMENT_NO,
                                    string.Format(Properties.McStructureXmlFileEditorResources.MSG_ERROR_ELEMENT_NO_NOT_SPECIFIED_MODEL_OR_PLACEMENT, citem.ToNo));
                    }

                    if (eitemFrom == null || eitemTo == null)
                    {
                        continue;
                    }

                    int iFromX = 0, iFromY = 0, iToX = 0, iToY = 0;
                    PortItem portSend = new PortItem();
                    PortItem portReceive = new PortItem();
                    //もとの接続を取得
                    bool bConnUpdate = GetPortPosition(citem.ID, ref iFromX, ref iFromY, portSend, ref iToX, ref iToY, portReceive);
                    if (!bConnUpdate)
                    {
                        //ID採番
                        citem.ID = NewID("C");
                    }
                    if (!bConnUpdate || (iFromX != eitemFrom.X) || (iFromY != eitemFrom.Y) || (iToX != eitemTo.X) || (iToY != eitemTo.Y))
                    {   //新規かFromToのモデル配置が変更されている場合、端子つけなおし
                        //送信端子
                        portSend.ID = NewID("P");
                        SetDataPort(portSend, eitemFrom, eitemTo);
                        //受信端子
                        portReceive.ID = NewID("P");
                        SetDataPort(portReceive, eitemTo, eitemFrom);
                    }

                    //送信端子
                    citem.From = portSend.ID;
                    eitemFrom.SendPorts.Add(iR - 1, portSend);
                    //受信端子
                    citem.To = portReceive.ID;
                    eitemTo.ReceivePorts.Add(iR - 1, portReceive);

                    //Connectionノード作成
                    XmlElement elmConnection = CreateConnection(xmlDoc, citem);
                    lstNewCon.Add(elmConnection);
                }

                if (m_csErrorList.Count > 0)
                {   //エラーがある
                    return false;
                }
                

                //CalElementノード作成
                foreach (CalElementItem eitem in lstCalElement.Values)
                {
                    XmlElement elmCalElement = CreateCalElement(xmlDoc, eitem);
                    lstNewCal.Add(elmCalElement);
                }
                AppendChildNodes(xmlDoc, lstNewCal, StructFileController.XML_TAG_COMPONENTS);

                //Connectionノード作成
                AppendChildNodes(xmlDoc, lstNewCon, StructFileController.XML_TAG_WIRING);


                //上書き保存
                xmlDoc.Save(sOutputXmlFileNamePath);

                return true;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex);
                //構造定義ファイル出力でエラーが発生しました。
                throw new Exception(Properties.McStructureXmlFileEditorResources.MSG_ERROR_OUTPUT_STRUCTURE_DEFINITION + "\n\n" + ex.Message);
            }
            finally
            {
                //Excelｱﾌﾟﾘｹｰｼｮﾝ終了
                csExcel.AppQuit();

                //COM オブジェクト解放
                csExcel.Dispose();
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>[①モデル配置]シートデータ取得</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SortedList csList = GetArea(csExcel, iRowCount, iColCount);</para>
        /// </example>
        /// <param name="csExcel">ExcelController</param>
        /// <param name="iRowCount">行数</param>
        /// <param name="iColCount">列数</param>
        /// <returns>モデル配置リスト[key=要素No, value=セル位置X,セル位置Y]</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private SortedList<int, int[]> GetArea(ExcelController csExcel, int iRowCount, int iColCount)
        {
            SortedList<int, int[]> lstArea = new SortedList<int, int[]>();

            object[,] oArea = csExcel.GetRange(AREASHEET_HEADERROW + 1, 1, iRowCount, iColCount);

            //配置を格納
            for (int iR = 1; iR <= oArea.GetUpperBound(0); iR++)
            {
                for (int iC = 1; iC <= oArea.GetUpperBound(1); iC++)
                {
                    int iNo = Convert.ToInt32(oArea[iR, iC]);           //要素No
                    if (iNo != 0)
                    {
                        int[] pitem = new int[] { iC - 1, iR - 1 };
                        if (lstArea.ContainsKey(iNo))
                        {   //同じ要素Noが入力されているエラー
                            //同じ要素Noが入力されています。
                            AddError(csExcel.xlSheet.Name, iR + 1, iC, Properties.McStructureXmlFileEditorResources.ELEMENT_NO,
                                Properties.McStructureXmlFileEditorResources.MSG_ERROR_SAME_ELEMENT_NO_INPUTTED);
                        }
                        else
                        {
                            lstArea.Add(iNo, pitem);
                        }
                    }
                }
            }

            return lstArea;
        }

        /// <summary><para>method outline:</para>
        /// <para>端子の向きと位置をセット</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SetDataPort(port, eitem, eitem2);</para>
        /// </example>
        /// <param name="port">対象のPortItem</param>
        /// <param name="eitem">対象のCalElementItem</param>
        /// <param name="eitem2">相手のCalElementItem</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private void SetDataPort(PortItem port, CalElementItem eitem, CalElementItem eitem2)
        {
            int iX = (int)(eitem.Width / 2.0 + 7);
            int iY = (int)(eitem.Height / 2.0 + 7);
            if (eitem.Rotation == 90 || eitem.Rotation == 270)
            {   //要素が左・右へ90度回転している場合、幅と高さを入れ替える
                iX = (int)(eitem.Height / 2.0 + 7);
                iY = (int)(eitem.Width / 2.0 + 7);
            }

            if (eitem2.CellX > eitem.CellX)
            {   //端子：右
                port.Direction = 90;
                port.X = eitem.X + iX;
                port.Y = eitem.Y;
            }
            else if (eitem2.CellX < eitem.CellX)
            {   //端子：左
                port.Direction = 270;
                port.X = eitem.X - iX;
                port.Y = eitem.Y;
            }
            else if (eitem2.CellY > eitem.CellY)
            {   //端子：下
                port.Direction = 180;
                port.X = eitem.X;
                port.Y = eitem.Y + iY;
            }
            else if (eitem2.CellY < eitem.CellY)
            {   //端子：上
                port.Direction = 0;
                port.X = eitem.X;
                port.Y = eitem.Y - iY;
            }
        }



    }
}
