// <summary>ソースコード：テストCSVファイル出力モデルクラス</summary>
// <author>CommonMP</author>

using System;
using System.Collections.Generic;
using System.Text;

using CommonMP.HYSSOP.CoreImpl.HSData;
using CommonMP.HYSSOP.CoreImpl.HSTools;
using CommonMP.HYSSOP.Interface.HSData;

using CommonMP.HYMCO.Interface.Model;
using CommonMP.HYMCO.Interface.Data;
using CommonMP.HYMCO.CoreImpl.Data;
using CommonMP.HYMCO.CoreImpl.Tool;
using CommonMP.HYMCO.CoreImpl.Model;

namespace CommonMP.HYMCO.OptionImpl.McSampleModelForDeveloper
{
    /// <summary><para>class outline:</para>
    /// <para>テスト用ＣＳＶファイル出力</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2008/10/01][新規作成]</para>
    /// <para>remarks:</para>
    /// <para>１次元配列情報をＣＳＶ形式のファイルに出力する：
    /// 　　　　出力する情報は　１変数のみ</para>
    /// </remarks>
    public class McTestOutFileModel : McBasiclFileOutCtl
    {
        /// <summary> 内部変数 </summary>
        McTestOutFileInfo m_csOutFileInfo = new McTestOutFileInfo();


        /// <summary><para>method outline:</para>
        /// <para>入力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ReceiveConnectionCheck(ref csErrorInf)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>受信するデータが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool ReceiveConnectionCheck(ref McStructErrorInfo csErrorInf, long lInputDataNum, McReceiveCellDataIF[] csInputCellData)
        {
            bool bRtn = true;

            McTimeSeriesD1CellArrayTranInfo csCellD1Trn = null;

            // 入力端子数のチェック
            if (m_lInputDataNum == 0)
            {
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND, "No Receive Port");
                bRtn = false;
            }
            else if (m_lInputDataNum > 1)
            {
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND, "Too many Receive Ports. (Not support multi-Receive Ports)");
                bRtn = false;
            }
            else if (m_lInputDataNum == 1)
            {

                if ((csCellD1Trn = m_csInputData[0] as McTimeSeriesD1CellArrayTranInfo) != null)
                {   // 入力情報種別のチェック
                    if (m_csOutFileInfo.m_lOutDataIdx >= csCellD1Trn.GetDataDimentionInCell())
                    {
                        // 指定した出力情報のインデックスが伝送データ内のセルの情報数より大きければエラー
                        bRtn = false;
                        csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND
                            , "Output Index is larger than data dimension in the cell. (Received from " + m_csInputData[0].GetUpperElementID().ToString() + ")");
                    }
                    
                }
                else
                {
                    bRtn = false;
                    csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND
                        , "Unexpected receive data type. (Received from " + m_csInputData[0].GetUpperElementID().ToString() + ")");
                }
            }
            return bRtn;
        }

        //=======================
        // 演算実行処理関連メソッド
        //=======================

        /// <summary><para>method outline:</para>
        /// <para>モデルを初期化する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Initialize(csInitialData)</para>
        /// </example>
        /// <param name="csInitialData">初期化設定情報</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override void Initialize(ref McPropertyInfoRoot csInitialData, long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            // 初期化情報
            McInitialInfo csRtnDt = csInitialData as McInitialInfo;
            if (csRtnDt != null)
            {   // 初期化情報あり
                // 出力時間間隔を取得
                double dDt = 0.0;
                csRtnDt.GetInfo("OUTTIME_LAG", ref dDt);
                m_csOutFileInfo.m_csOutTimeLag = new HySTime(dDt * 3600.0);
            }

            // 情報の初期化
            m_csOutFileInfo.m_csLastINTime = new HySTime(0);
            m_csOutFileInfo.m_csLastOutTime = new HySTime(0);


            McTimeSeriesD1CellArrayTranInfo csCellD1Trn = null;

            if ((csCellD1Trn = m_csInputData[0] as McTimeSeriesD1CellArrayTranInfo) != null)
            {   // セル１次元配列情報ならば
                m_csOutFileInfo.m_lDtDim = csCellD1Trn.GetDimension1();
                // 配列数を保持しておく
            }
            else
            {
            }

            // 出力ファイル設定
            m_csOutFile = new HySFile(m_csOutFileInfo.m_csOutFileNameWithPath);
            
            // 出力ファイル初期生成
            m_csOutFile.Open(HySFile.OPEN_MODE.CREATE_NEW, HySFile.READ_WRITE_MODE.WRITE, HySFile.DIRECTORY_MODE.MK_DIR);
            // ヘッダーを作成
            m_csOutFile.WriteText("HySLineGraphData,Ver1.0");  // ヘッダー
            m_csOutFile.WriteText("データ区分, 時系列");  // ヘッダー
            String sWString = "Time";
            for (int iLp = 0; iLp < m_csOutFileInfo.m_lDtDim; iLp++)
            {
                sWString += ", Data" + iLp.ToString(); 
            }
            m_csOutFile.WriteText(sWString);
            m_csOutFile.Close();
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル演算結果を外部のエレメントに対して公開する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = DataFusion( lOutputDataNum, ref csOutputCellData)</para>
        /// </example>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long DataFusion(long lOutputDataNum, ref McSendCellDataIF[] csOutputCellData) 
        {

            // データ取得
            this.ReadData();

            long lDtNum = m_csOutFileInfo.m_csDataList.Count;  // 保管中の情報の数

            LinkedListNode<McTestOutRecord> csDtNode;

            McTestOutRecord csData = null;
            HySTime csTm = null;
            string sStr = "";

            for (int iLp = 0; iLp < lDtNum; iLp++)
            {   // 保管中の情報分繰り返す

                // 保管情報を１レコードづつ取り出す
                csDtNode = m_csOutFileInfo.m_csDataList.First;
                csData = csDtNode.Value;

                csTm = csData.m_csTime; // レコードの時刻
                if ((m_csOutFileInfo.m_csLastOutTime + m_csOutFileInfo.m_csOutTimeLag).After(csTm) == false)
                {   // 出力すべきレコード時刻か？

                    if (m_csOutFile.Open(HySFile.OPEN_MODE.APPEND, HySFile.READ_WRITE_MODE.WRITE, HySFile.DIRECTORY_MODE.NOT_MK_DIR) == 0)
                    {   // 出力ファイルオープン
                        
                        // ファイルへ書き込み
                        sStr = HySCalendar.GetString(csData.m_csTime, HySCalendar.FORMAT.lSW_YEAR).ToString();
                        for (int iDtLp = 0; iDtLp < m_csOutFileInfo.m_lDtDim; iDtLp++)
                        {
                            sStr += "," + (csData.m_dDT[iDtLp]).ToString();
                        }
                        m_csOutFile.WriteText(sStr);
                        m_csOutFileInfo.m_csLastOutTime.SetTime(csTm);

                        McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "DataFusion", " WriteDataNum=" + lDtNum.ToString() + " LastWriteString= " + sStr);
                        
                        // 出力ファイルを閉じる
                        m_csOutFile.Close();
                    }
                }

                // 読み出した情報は一時保管情報から削除する
                m_csOutFileInfo.m_csDataList.RemoveFirst();
            } 
  
            return 0;
        }

        /// <summary><para>method outline:</para>
        /// <para>入力された伝送データの取得し一時保管しておく</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ReadData()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected virtual long ReadData()
        {
            McTimeSeriesD1CellArrayTranInfo csCellD1Trn = null;
            McD1CellArrayTranInfo csDtRcd = null;
            HySCellData[] csCells = null;

            // 入力された伝送情報を読み出す
            if ((csCellD1Trn = m_csInputData[0] as McTimeSeriesD1CellArrayTranInfo) != null)
            {   
                McTestOutRecord csRcd = null;  // １レコード分のデータ

                long lDim = csCellD1Trn.GetDimension(); // 配列の大きさは？
                long lDtNum = csCellD1Trn.SetCursorPlus(m_csOutFileInfo.m_csLastINTime);// 最終時刻以後に情報が追加されているか？

                for (long lDtLp = 0; lDtLp < lDtNum; lDtLp++)
                {   // 追加された情報分処理を行う

                    csDtRcd = (McD1CellArrayTranInfo)csCellD1Trn.GetCursorData();
                    csCells = csDtRcd.GetCellData();
                    csRcd = new McTestOutRecord(lDim);  // １レコード情報格納クラスの生成
                    for (long lDt = 0; lDt < lDim; lDt++)
                    {   // １レコード分の情報を　設定する
                        csRcd.m_dDT[lDt] = csCells[lDt].m_dData[m_csOutFileInfo.m_lOutDataIdx];
                    }
                    csRcd.m_csTime = csDtRcd.GetTime();

                    // 読み出した情報を一時保管しておく
                    m_csOutFileInfo.m_csDataList.AddLast(csRcd);
                    m_csOutFileInfo.m_csLastINTime.SetTime(csRcd.m_csTime);

                    csCellD1Trn.MoveCursorNext();
                }
            }
            else
            {
            }

            return 0;
        }

        //====================
        // その他必要なメソッド
        //====================

        /// <summary><para>method outline:</para>
        /// <para>プロパティ情報を設定する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SetProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">セル型プロパティ情報</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override void SetProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            McModelPropertyInfo csPropInf = csCellMdlPropertyInfo as McModelPropertyInfo;
            if (csPropInf != null)
            {   // プロパティ情報あり

                // 出力ファイルの設定
                csPropInf.GetInfo("m_csOutFileNameWithPath", ref this.m_csOutFileInfo.m_csOutFileNameWithPath);

                csPropInf.GetInfo("m_lOutDataIdx", ref this.m_csOutFileInfo.m_lOutDataIdx);
            }
            else
            {
                this.m_csOutFileInfo.m_csOutFileNameWithPath = HySEnvInf.GetHomeDirectory().ToString() + "\\temp\\TestFileDfltOut.csv";
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>シミュレーションデータコンテナの内容を設定する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SetSimDataContainerInfo( csDataContainer )</para>
        /// </example>
        /// <param name="csDataContainer">シミュレーションデータコンテナ</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override void SetSimDataContainer(HySSimulationDataContainer csDataContainer)
        {
            // 何もしない
        }

    }
}
