// <summary>ソースコード：マージモデルクラス</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.HYMCO.Interface;
using CommonMP.HYMCO.Interface.Model;
using CommonMP.HYMCO.Interface.Data;

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

namespace CommonMP.HYMCO.OptionImpl.McMergeForDevelop   
{
    /// <summary><para>class outline:</para>
    /// <para>マージモデルクラス</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2012/01/13][新規作成]</para>
    /// </remarks>
    public class McMergeModel : McStateCalModelBase
    {
        /// <summary> 演算データ（キャスト用） </summary>
        McMergeCalInfo m_csMergeInf = null;   // 便利の為、キャスト用に定義しておく
        bool m_bCheck = false;

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


        /// <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;
            //// <summary>データの次元数</summary>
            long lDimNum = 0;
            //// <summary>配列数：Ｄｉｍ１ </summary>
            long lDim1 = 0;
            //// <summary>配列数：Ｄｉｍ２ </summary>
            long lDim2 = 0;
            //// <summary>配列数：Ｄｉｍ３ </summary>
            long lDim3 = 0;
            //// <summary>セル内の変数の数</summary>
            long lDataDimInCell = 0;

            for (long lLp = 0; lLp < lInputDataNum; lLp++)
            {   // 入力する伝送データ数分繰り返します。
                HySObjectKind csObjKind = csInputCellData[lLp].GetTranInfoKind();
                if (csObjKind.Equals(McTranInfoDefine.D1_CELL_SERIAL) == false)
                {   // 1次元時系列以外はエラー
                    bRtn = false;
                   // ver1.5 エラートレース日本語対応
                    csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                        McMergeDefine.MERGE_MODEL_KIND,
                        Properties.MyModelResources.STATEMENT_UNEXPECT_REC_DATA_TYPE_R + csInputCellData[lLp].GetUpperElementID().ToString() + ")");
                   // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                   //     McMergeDefine.MERGE_MODEL_KIND,
                   //     "Unexpected receive data type. (Received from " + csInputCellData[lLp].GetUpperElementID().ToString() + ")");
                }
                else
                {
                    // データの次元を取得
                    if (lLp == 0)
                    {
                        m_csMergeInf.m_lDimNum = csInputCellData[lLp].GetDimension(ref m_csMergeInf.m_lDim1, ref m_csMergeInf.m_lDim2, ref m_csMergeInf.m_lDim3, ref m_csMergeInf.m_lDataDimInCell);
                    }
                    else
                    {
                        lDimNum = csInputCellData[lLp].GetDimension(ref lDim1, ref lDim2, ref lDim3, ref lDataDimInCell);
                        if (lDim1 != m_csMergeInf.m_lDim1 || lDataDimInCell != m_csMergeInf.m_lDataDimInCell)
                        {
                            bRtn = false;
                           // ver1.5 エラートレース日本語対応
                            csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                                McMergeDefine.MERGE_MODEL_KIND,
                                Properties.MyModelResources.STATEMENT_INPUTDATA_DIM_MIS );
                           // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                           //     McMergeDefine.MERGE_MODEL_KIND,
                           //     "Input Data Dimension mismatch.");
                        }
                    }
                    
                }


            }
            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>出力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SendConnectionCheck(ref csErrorInf)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>送信端子に設定されている伝送データが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool SendConnectionCheck(ref McStructErrorInfo csErrorInf, long lOutputDataNum, McSendCellDataIF[] csOutputCellData)
        {
            bool bRtn = true;
            //// <summary>データの次元数</summary>
            long lDimNum = 0;
            //// <summary>配列数：Ｄｉｍ１ </summary>
            long lDim1 = 0;
            //// <summary>配列数：Ｄｉｍ２ </summary>
            long lDim2 = 0;
            //// <summary>配列数：Ｄｉｍ３ </summary>
            long lDim3 = 0;
            //// <summary>セル内の変数の数</summary>
            long lDataDimInCell = 0;

            for (long lLp = 0; lLp < lOutputDataNum; lLp++)
            {   // 出力する伝送データ数分繰り返します。
                HySObjectKind csObjKind = csOutputCellData[lLp].GetTranInfoKind();
                if (csObjKind.Equals(McTranInfoDefine.D1_CELL_SERIAL) == false)
                {   // 1次元時系列以外はエラー
                    bRtn = false;
                   // ver1.5 エラートレース日本語対応
                    csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                        McMergeDefine.MERGE_MODEL_KIND,
                        Properties.MyModelResources.STATEMENT_UNEXPECT_SEND_DATA_PATTERN_S + csOutputCellData[lLp].GetLowerElementID().ToString() + ")");
                   // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                   //     McMergeDefine.MERGE_MODEL_KIND,
                   //     "Unexpected send data Pattern. (send to " + csOutputCellData[lLp].GetLowerElementID().ToString() + ")");
                }
                else
                {
                    lDimNum = csOutputCellData[lLp].GetDimension(ref lDim1, ref lDim2, ref lDim3, ref lDataDimInCell);
                    if (lDim1 != m_csMergeInf.m_lDim1 || lDataDimInCell != m_csMergeInf.m_lDataDimInCell)
                    {
                        bRtn = false;
                       // ver1.5 エラートレース日本語対応
                        csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                            McMergeDefine.MERGE_MODEL_KIND,
                            Properties.MyModelResources.STATEMENT_OUTPUTDATA_DIM_MIS );
                       // csErrorInf.AddCheckErrorData(this.m_csElement.GetID(),
                       //     McMergeDefine.MERGE_MODEL_KIND,
                       //     "Output Data Dimension mismatch.");
                    }
                }
            }
            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)
        {
            bool bRtn = false;

            //m_csMergeInf.m_csMargedDataObj = new McTimeSeriesD1CellArrayTranInfo(m_csMergeInf.m_lDim1, m_csMergeInf.m_lDataDimInCell, 96);
            m_csMergeInf.m_LastInTime = HySTime.DEFAULT_TIME.Clone() + new HySTime(1);
            m_csMergeInf.m_LastOutTime = HySTime.DEFAULT_TIME.Clone() + new HySTime(1);
            // 引数で与えられたデータを　キャストしています。
            McInitialInfo csInDt = csInitialData as McInitialInfo;

            if (csInDt != null)
            {
                bRtn = true;
                // To Do
                // 演算データ等で初期化が必要な情報に対して、引数で与えられた初期化情報の内容を読み取って、設定します。
            }
            return bRtn;
        }

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

        /// <summary><para>method outline:</para>
        /// <para>モデル演算</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = Calculate(ref csInputCellData)</para>
        /// </example>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long Calculate(long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
// debug
            string sTime= HySCalendar.ToString(m_csSimTime, HySCalendar.FORMAT.lSW_YEAR);
            //McLog.DebugOut(m_csSimTime, this.GetID(), "McMergeModel", "Calculate", "start");

            //double dPast = 0.0; // 過去データ
            //double dNew  = 0.0; // 最新データ

            if (!m_bCheck)
            {
                m_bCheck = true;
            }

            // 入力データを取得
            for (long lLp = 0; lLp < lInputDataNum; lLp++)
            {
                if (m_csMergeInf.m_LastInTime.Before(csInputCellData[lLp].GetLastTime()) == true)
                {
// debug
//McLog.DebugOut(m_csSimTime, this.GetID(), "McMergeModel", "Calculate", "Read Data");

                    HySCellData[] csCellData = csInputCellData[lLp].GetInterpolatedCellD1();
                    HySID csInputPtnID = csInputCellData[lLp].GetReceivePatternID();
                    double dValue = csInputCellData[lLp].Data(csCellData[0], 0);
                    if (csInputPtnID.Equals(McMergeDefine.IN_PATTERN_PAST) == true)
                    {
                        //dPast = dValue;
                        
                        m_csMergeInf.m_dOut = dValue;
                        //m_csMergeInf.m_LastInTime.SetTime(this.m_csSimTime);
                    }
                    else if (csInputPtnID.Equals(McMergeDefine.IN_PATTERN_NEW) == true)
                    {
                        //dNew = dValue;

                        m_csMergeInf.m_dOut = dValue;
                        //m_csMergeInf.m_LastInTime.SetTime(this.m_csSimTime);
                    }
                }
            }
            m_csMergeInf.m_LastInTime.SetTime(this.m_csSimTime);

            //McLog.DebugOut(GetSimulationTime(), GetID(), "McMergeModel", "Calculate", string.Format("in1:{0} in2:{1}", dPast.ToString(), dNew.ToString()));
            return 0;
        }


        /// <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)
        {
// debug
string sTime = HySCalendar.ToString(m_csMergeInf.m_LastInTime, HySCalendar.FORMAT.lSW_YEAR);
//McLog.DebugOut(m_csSimTime, this.GetID(), "McMergeModel", "DataFusion", "start");

            if (m_csMergeInf.m_LastOutTime.Before(m_csMergeInf.m_LastInTime) == true)
            {
                for (long lLp = 0; lLp < lOutputDataNum; lLp++)
                {   // 出力する伝送データ数分繰り返します。
                    csOutputCellData[lLp].SetCurrentTime(m_csMergeInf.m_LastInTime);

                    HySCellData[] csSndCellData = csOutputCellData[lLp].PrepareSendCellD1();
                    csSndCellData[0].m_dData[0] = m_csMergeInf.m_dOut;

                    csOutputCellData[lLp].SendData();
                }
                m_csMergeInf.m_LastOutTime.SetTime( m_csMergeInf.m_LastInTime);
            }

            
            //m_csMergeInf.m_csMargedDataObj.Clear();
            return 0;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="csOutputDataList"></param>
        /// <returns></returns>
        public override long DataFusion(ref HySDataLinkedList csOutputDataList)
        {
            DataFusion(m_lOutputDataNum, ref m_OutputCellData);

            // 此処まで計算が終了し、外部公開した。従って、演算完了シミュレーション時刻を設定できる
            m_csCalDoneSimulationTime.SetTime(m_csSimTime);


            // データフュージョンの後シミュレーション時刻を進める
            m_csSimTime.Add(m_csDltTime);
            m_csTotalPassingTime.SetTime(m_csSimTime.GetTime() - m_csStartTime.GetTime());

            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)
        {
            bool bRtn = false;
            // 使用しやすいようにキャストしておく
            m_csMergeInf = (McMergeCalInfo)m_csCalInfo;

            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                // 演算ステップ時刻設定
                this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());

                bRtn = true;

                // To Do
                // モデルの係数等、必要な情報に対して、引数で与えられたプロパティ情報の内容を読み取って、設定します。
            }
            return bRtn;
        }

        /// <summary><para>method outline:</para>
        /// <para>計算開始時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ReadyCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>計算開始の　最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool ReadyCalculation()
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "McMergeModel", "ReadyCalculation", "in");

            // ToDo
            // 計算開始の　最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // （メソッド自身を削除してください）

            //McLog.DebugOut(GetSimulationTime(), GetID(), "McMergeModel", "ReadyCalculation", "out");
            m_bCheck = false;
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override bool IsConverged()
        {
            /*
            bool bRtn = false;
            if (m_csSimTime.Before(m_csTgtTime) != true)
            {   // 計算時刻が、目標時刻を超えたならば
                bRtn = true;
            }
            return bRtn;
            */
            bool bRtn = false;

            if (m_csSimTime.After(m_csTgtTime) == true)
            {   // 計算時刻が、目標時刻を超えたならば
                if (m_csSimTime.Before(m_csFinalGoalTime) != true &&
                    m_csFinalGoalTime.GetTime() > (m_csSimTime.GetTime() - m_csDltTime.GetTime()))
                {   // 最終目標時刻を計算していないならば、最終計算時刻の計算を行う
                    m_csOrgDltTime.SetTime(m_csDltTime);
                    m_csDltTime.SetTime(m_csFinalGoalTime.GetTime() - (m_csSimTime.GetTime() - m_csDltTime.GetTime()));
                    m_csSimTime.SetTime(m_csFinalGoalTime);
                }
                else
                {
                    bRtn = true;
                }
            }
            /*
            if (bRtn == false)
            {   // 先の条件で収束していないとき
                // 上流の全ての要素が必要時刻まで計算されていなければ計算を開始しない
                // 即ち　IsConverged=true で返す。（計算が行われない為、シミュレーション時刻も進行しない）
                if (m_csSimTime.TotalSec() > m_csFinalGoalTime.TotalSec())  // シミュレーションが最後まで実行された
                {
                    bRtn = true;
                    return bRtn;
                }
                for (long lP = 0; lP < m_lInputDataNum; lP++)
                {   // 入力データ数分
                    //HySID csInputPtnID = m_InputCellData[lP].GetReceivePatternID();
                    if (m_bCheck)
                    {
                        continue;
                    }
                    //else if (m_csSimTime.After(m_InputCellData[lP].GetLastTime()) == true)
                    if (m_csSimTime.After(m_InputCellData[lP].GetLastTime()) == true)
                    {
                        bRtn = true;
                    }
                }
            }
            */
            return bRtn;
        }
    }
    //
}
