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

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

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

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;
using CommonMP.HYMCO.CoreImpl.Data.FileIO;

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 : McBasicFileOutCtl
    {
        /// <summary> 内部変数 </summary>
        McTestOutFileInfo m_csOutFileInfo = null; //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;

            // 入力端子数のチェック
            if (lInputDataNum == 0)
            {
                csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND, "No Receive Port");
                bRtn = false;
            }
            else if (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 (lInputDataNum == 1)
            {
                long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                long lCellDim = csInputCellData[0].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);
                m_csOutFileInfo.m_lDimNum = lCellDim;
                m_csOutFileInfo.m_lDtDim1 = lD1;
                m_csOutFileInfo.m_lDtDim2 = lD2;
                m_csOutFileInfo.m_lDtDim3 = lD3;
                if (lCellDim == 1 && lD1 >= 1)
                {   // 1次元データならば
                    if (m_csOutFileInfo.m_lOutDataIdx >= lCellDataNum)
                    {
                        // 指定した出力情報のインデックスが伝送データ内のセルの情報数より大きければエラー
                        bRtn = false;
                        csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND
                            , "Output variable's index is larger than number of variables in the received cell. (Received from " + m_csInputData[0].GetUpperElementID().ToString() + ")");
                    }
                }
                else if (lCellDim == 2 && lD1 >= 1 && lD2 >=1 )
                {   // ２次元データならば
                    if (m_csOutFileInfo.m_lOutDataIdx >= lCellDataNum)
                    {
                        // 指定した出力情報のインデックスが伝送データ内のセルの情報数より大きければエラー
                        bRtn = false;
                        csErrorInf.AddCheckErrorData(this.m_csElement.GetID(), McModelSampleDefine.TEST_FILE_OUT_KIND
                            , "Output variable's index is larger than number of variables in the received 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 bool 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_csOutTimeLag = new HySTime(dDt * 1.0);
            }

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

            McTimeSeriesD2CellArrayTranInfo csCellD2Trn = null;
            McTimeSeriesD1CellArrayTranInfo csCellD1Trn = null;
            McTimeSeriesSingleCellTranInfo  csSinglCellTrn = null;

            if ((csSinglCellTrn = m_csInputData[0] as McTimeSeriesSingleCellTranInfo) != null)
            {   // シングルセル情報ならば
                m_csOutFileInfo.m_lDtDim1 = 1;
                // 配列数を保持しておく
            }
            else if ((csCellD1Trn = m_csInputData[0] as McTimeSeriesD1CellArrayTranInfo) != null)
            {   // セル１次元配列情報ならば
                m_csOutFileInfo.m_lDtDim1 = csCellD1Trn.GetDimension1();
                // 配列数を保持しておく
            }
            else if ((csCellD2Trn = m_csInputData[0] as McTimeSeriesD2CellArrayTranInfo) != null)
            {   // セル１次元配列情報ならば
                m_csOutFileInfo.m_lDtDim1 = csCellD2Trn.GetDimension1();
                m_csOutFileInfo.m_lDtDim2 = csCellD2Trn.GetDimension2();
                // 配列数を保持しておく
            }
            else
            {
                m_csOutFileInfo.m_lDtDim1 = 0;
            }

            // 出力ファイル設定
            m_csOutFile = new HySFile(m_csOutFileNameWithFullPath.ToString());
            
            // 出力ファイル初期生成
            m_csOutFile.Open(HySFile.OPEN_MODE.CREATE_NEW, HySFile.READ_WRITE_MODE.WRITE, HySFile.DIRECTORY_MODE.MK_DIR);
            // ヘッダーを作成
            if (m_csOutFileInfo.m_lDimNum == 1)
            {   // １次元時系列データならば
                m_csOutFile.WriteText("HySCSVFileData,Ver1.0");  // ヘッダー
                m_csOutFile.WriteText("データ区分, 時系列");  // ヘッダー
                String sWString = "Time";
                for (int iLp = 0; iLp < m_csOutFileInfo.m_lDtDim1; iLp++)
                {
                    sWString += ", Data" + iLp.ToString(); 
                }
                m_csOutFile.WriteText(sWString);
            }
            else if (m_csOutFileInfo.m_lDimNum == 2)
            {   // ２次元時系列データならば
                m_csOutFile.WriteText("HySCSVFileDim2Data,Ver1.0");  // ヘッダー
                m_csOutFile.WriteText("データ区分, 時系列");  // ヘッダー
                String sWString = "配列数,";
                sWString += m_csOutFileInfo.m_lDtDim1.ToString() + ",×," + m_csOutFileInfo.m_lDtDim2.ToString();
                m_csOutFile.WriteText(sWString);
                m_csOutFile.WriteText("Time, Data");
            }

            m_csOutFile.Close();
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>モデルを初期化する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>OnlineInitialize(csInitialData)</para>
        /// </example>
        /// <param name="csInitialData">初期化設定情報</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>bool</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override bool OnlineInitialize(ref McPropertyInfoRoot csInitialData, long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            return true;
        }
        /// <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;
            McTestOutD2Record csD2Data = null;
            //McTestOutD3Record csD3Data = 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)
                    {   // 出力ファイルオープン
                        // ファイルへ書き込み
                        if (m_csOutFileInfo.m_lDimNum == 1)
                        {   // １次元データならば
                            sStr = HySCalendar.GetString(csData.m_csTime, HySCalendar.FORMAT.lSW_YEAR).ToString();
                            for (int iDtLp = 0; iDtLp < m_csOutFileInfo.m_lDtDim1; iDtLp++)
                            {
                                sStr += "," + (csData.m_dDT[iDtLp]).ToString();
                            }
                            m_csOutFile.WriteText(sStr);
                        }
                        else if (m_csOutFileInfo.m_lDimNum == 2)
                        {   // ２次元データならば
                            csD2Data = csData as McTestOutD2Record;
                            sStr = HySCalendar.GetString(csData.m_csTime, HySCalendar.FORMAT.lSW_YEAR).ToString();
                            m_csOutFile.WriteText(sStr);
                            for (int iDtP2 = 0; iDtP2 < m_csOutFileInfo.m_lDtDim2; iDtP2++)
                            {
                                sStr = "";
                                for (int iDtLp = 0; iDtLp < m_csOutFileInfo.m_lDtDim1; iDtLp++)
                                {
                                    sStr += "," + (csD2Data.m_dD2DT[iDtLp, iDtP2]).ToString();
                                }
                                m_csOutFile.WriteText(sStr);
                            }
                        }
                        //else if (m_csOutFileInfo.m_lDimNum == 3)
                        //{
                        //    csD3Data = csData as McTestOutD3Record;
                        //}
                        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;
            McTimeSeriesD2CellArrayTranInfo csCellD2Trn = null;
            McTimeSeriesSingleCellTranInfo csSinglCellTrn = null;
            // 入力された伝送情報を読み出す
            if ((csCellD1Trn = m_csInputData[0] as McTimeSeriesD1CellArrayTranInfo) != null)
            {   // １次元の場合
                McD1CellArrayTranInfo csDtRcd = null;
                HySCellData[] csCells = null;
                McReceiveCellDataIF csInputCellData = m_InputCellData[0];
                McTestOutRecord csRcd = null;  // １レコード分のデータ

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

                //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                //    "ReadDataNum=" + lDtNum.ToString() + "    LastIntime=" + (HySCalendar.GetString(m_csOutFileInfo.m_csLastINTime, HySCalendar.FORMAT.lSW_DAY)).ToString());

                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] = csInputCellData.Data(csCells[lDt], m_csOutFileInfo.m_lOutDataIdx);
                    }
                    csRcd.m_csTime = csDtRcd.GetLastTime();

                    //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                    //    " ReadDataTime=" + (HySCalendar.GetString( csDtRcd.GetTime(), HySCalendar.FORMAT.lSW_DAY)).ToString() + " Dt[0]= " + csRcd.m_dDT[0].ToString());

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

                    csCellD1Trn.MoveCursorNext();
                }
            }
            else if ((csCellD2Trn = m_csInputData[0] as McTimeSeriesD2CellArrayTranInfo) != null)
            {   // ２次元の場合
                McD2CellArrayTranInfo csDtD2Rcd = null;
                HySCellData[,] csD2Cells = null;
                McReceiveCellDataIF csInputCellData = m_InputCellData[0];
                McTestOutD2Record csRcd = null;  // １レコード分のデータ

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

                //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                //    "ReadDataNum=" + lDtNum.ToString() + "    LastIntime=" + (HySCalendar.GetString(m_csOutFileInfo.m_csLastINTime, HySCalendar.FORMAT.lSW_DAY)).ToString());

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

                    csDtD2Rcd = (McD2CellArrayTranInfo)csCellD2Trn.GetCursorData();
                    csD2Cells = csDtD2Rcd.GetCellData();
                    csRcd = new McTestOutD2Record(lDim1,lDim2);  // １レコード情報格納クラスの生成
                    for (long lDt = 0; lDt < lDim1; lDt++)
                    {
                        for (long lDt2 = 0; lDt2 < lDim2; lDt2++)
                        {
                            // １レコード分の情報を　設定する
                            csRcd.m_dD2DT[lDt, lDt2] = csInputCellData.Data(csD2Cells[lDt, lDt2], m_csOutFileInfo.m_lOutDataIdx);
                        }
                    }
                    csRcd.m_csTime = csDtD2Rcd.GetLastTime();

                    //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                    //    " ReadDataTime=" + (HySCalendar.GetString( csDtRcd.GetTime(), HySCalendar.FORMAT.lSW_DAY)).ToString() + " Dt[0]= " + csRcd.m_dDT[0].ToString());

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

                    csCellD2Trn.MoveCursorNext();
                }
            }
            else if ((csSinglCellTrn = m_csInputData[0] as McTimeSeriesSingleCellTranInfo) != null)
            {   // 単一セルの場合
                HySCellData csSingleCells = null;
                //McD1CellArrayTranInfo csDtRcd = null;

                McReceiveCellDataIF csInputCellData = m_InputCellData[0];
                McTestOutRecord csRcd = null;  // １レコード分のデータ

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

                //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                //    "ReadDataNum=" + lDtNum.ToString() + "    LastIntime=" + (HySCalendar.GetString(m_csOutFileInfo.m_csLastINTime, HySCalendar.FORMAT.lSW_DAY)).ToString());

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

                    csSingleCells = (HySCellData)csSinglCellTrn.GetCursorData();
                    csRcd = new McTestOutRecord(lDim);  // １レコード情報格納クラスの生成
                    // １レコード分の情報を　設定する
                    csRcd.m_dDT[0] = csInputCellData.Data(csSingleCells, m_csOutFileInfo.m_lOutDataIdx);

                    csRcd.m_csTime = csSinglCellTrn.GetCursorTime().Clone();

                    //McLog.DebugOut(GetSimulationTime(), m_csElement.GetID(), "McTestOutFileModel", "ReadData",
                    //    " ReadDataTime=" + (HySCalendar.GetString( csDtRcd.GetTime(), HySCalendar.FORMAT.lSW_DAY)).ToString() + " Dt[0]= " + csRcd.m_dDT[0].ToString());

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

                    csSinglCellTrn.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 bool SetProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            // 使用しやすいようにキャストしておく
            m_csOutFileInfo = (McTestOutFileInfo)m_csCalInfo;

            McModelPropertyInfo csPropInf = csCellMdlPropertyInfo as McModelPropertyInfo;
            if (csPropInf != null)
            {   // プロパティ情報あり

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

                //csPropInf.GetInfo("m_lOutDataIdx", ref this.m_csOutFileInfo.m_lOutDataIdx);
                this.m_csOutFileInfo.m_lOutDataIdx = 0; // 0固定
            }
            else
            {
                m_csOutFileNameWithFullPath = new HySString(HySEnvInf.GetHomeDirectory().ToString() + "\\temp\\TestFileDfltOut.csv");
            }
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>演算実行中断中のプロパティ等情報設定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SetOnlineProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">プロパティ情報</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>演算中断中にプロパティ、初期値等を変更して動作させ場合等に使用する</para>
        /// </remarks>
        public override bool SetOnlineProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                // To Do
                // モデルの係数等、必要な情報に対して、引数で与えられたプロパティ情報の内容を読み取って、設定していきます。
                // 但し、実行中に変更して困る情報は設定しないで下さい。
            }
            return true;
        }

    }
}
