// <summary>ソースコード：テスト用流量発生モデルクラス</summary>
// <author>CommonMP</author>

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

using CommonMP.HYSSOP.CoreImpl;
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;



namespace CommonMP.HYMCO.OptionImple.McHyHdFormulaSampleH13
{
    /// <summary><para>class outline:</para>
    /// <para>水理公式集例題２－２より　テスト用流量発生モデル</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>
    /// [CommonMP][ver 1.3.0][2012/13/11][新規作成]
    /// FORTRAN プログラム(kinematic.for, diffusion.for, dynamic.for) 内の
    /// !*****************************************************************************
    /// !
    /// !  FUNCTION: QIN
    /// !
    /// !  PURPOSE:  DISCHARGE AT THE UPSTREAM
    /// !
    /// !*****************************************************************************
    /// に相当するプログラム
    /// </para>
    /// </remarks>
    public class McTestBasin : McForecastModelBase
    {
        /// <summary>流域計算情報</summary>
        McTestBasinCalInfo m_csBasinInfo = null;

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

        /// <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.GetID(), McHyHdFormulaSampleH13Define.Q_GENERATOR_KIND, "Cannot receive data.");
                bRtn = false;
            }
            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;

            if (lOutputDataNum == 0)
            {
                // 警告（エラーではない）
                csErrorInf.AddCheckWarningData(this.GetID(), McHyHdFormulaSampleH13Define.Q_GENERATOR_KIND, "No send port.");
            }
            for (long lP = 0; lP < lOutputDataNum; lP++)
            {   // 出力側接続線数分チェックを行う
                if (McHyHdFormulaSampleH13Define.OUT_PATTERN_BOTTOM.Equals(csOutputCellData[lP].GetSendPatternID()) == true)
                {   // 最終セルだけの出力ならば
                    long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                    long lCellDim = csOutputCellData[lP].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);
                    if (lCellDim == 1 && lD1 == 1)
                    {   // １次元配列で１セルならば
                        if (lCellDataNum < 1)
                        {   // セル内の変数の数
                            csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.Q_GENERATOR_KIND,
                                "Variable number in the Cells is too short. (Send To " + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                            bRtn = false;
                        }
                    }
                    else
                    {
                        // エラー
                        csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.Q_GENERATOR_KIND,
                                                "Unexpected send data type. (Send To " + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                        bRtn = false;
                    }
                }
                else
                {
                    // エラー
                    csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.Q_GENERATOR_KIND,
                                            "Unexpected send data type. (Send To " + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                    bRtn = false;
                }
            }
            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)
        {
            // 使用しやすいようにキャストしておく
            //m_csBasinInfo = (McSampleBasinCalInfo)m_csCalInfo;
            if (csInitialData != null)
            {
                m_csBasinInfo.m_dTau = 0.0;

                m_csBasinInfo.m_dOutQ =
                    (m_csBasinInfo.m_dQb + (m_csBasinInfo.m_dQp - m_csBasinInfo.m_dQb)
                       * (Math.Pow((m_csBasinInfo.m_dTau * Math.Exp(1.0 - m_csBasinInfo.m_dTau)), m_csBasinInfo.m_dCp)
                         )
                    );
            }

            McLog.DebugOut(this.m_csSimTime, GetID(), "McTestBasin", "Initialize", "Initial value Q=" + m_csBasinInfo.m_dOutQ.ToString());
            return true;
        }

        /// <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)
        {
            // this.m_csTotalPassingTime には シミュレーション開始時刻からの経過時間が入っている
            m_csBasinInfo.m_dTau = this.m_csTotalPassingTime.GetTime() / m_csBasinInfo.m_csTp.GetTime();
            

            m_csBasinInfo.m_dOutQ =
                (m_csBasinInfo.m_dQb + (m_csBasinInfo.m_dQp - m_csBasinInfo.m_dQb)
                   * (Math.Pow((m_csBasinInfo.m_dTau * Math.Exp(1.0 - m_csBasinInfo.m_dTau)), m_csBasinInfo.m_dCp)
                     )
                );

            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)
        {
            HySCellData[] csSndCellData = null;
            for (long lP = 0; lP < m_lOutputDataNum; lP++)
            {
                csSndCellData = csOutputCellData[lP].PrepareSendCellD1();
                csSndCellData[0].m_dData[0] = m_csBasinInfo.m_dOutQ;
            }
            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_csBasinInfo = (McTestBasinCalInfo)m_csCalInfo;

            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                // 計算パラメーター
                csPrptyInfo.GetInfo("m_dQb", ref m_csBasinInfo.m_dQb);
                csPrptyInfo.GetInfo("m_dQp", ref m_csBasinInfo.m_dQp);
                csPrptyInfo.GetInfo("m_dCp", ref m_csBasinInfo.m_dCp);
                csPrptyInfo.GetInfo("m_dOutQ0", ref m_csBasinInfo.m_dOutQ);

                 // 演算ステップ時刻設定
                this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());
            }

            return true;
        }

    }
}
