// <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;

// ToDo namespace は　モデル開発者が変更して、ユニークな名称にして下さい
namespace CommonMP.HYMCO.OptionImple.McHyHdFormulaSampleH13
{
    /// <summary><para>class outline:</para>
    /// <para>水理公式集例題１－３より　時系列データ発生モデル</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.2.0][2011/11/11][新規作成]</para>
    /// </remarks>
    public class McTestDataGenerator : McStateCalModelBase
    {
        /// <summary>乱数発生クラス </summary>
        public static Random m_csRnd = new System.Random(DateTime.Now.GetHashCode());

        // 正規乱数発生用変数
        bool m_bGaussSW = true;
        double m_dv1, m_dv2, m_dBoxMuller1, m_dBoxMuller2;

        // ブラウン運動発生用変数
        double m_dGaussX = 0.0;

        // ＡＲ発生用変数
        double m_dARX = 0.0;
        double m_dR1 = 0.489;
        double m_dRx = 0.0;

        // ＡＲＭＡ発生用変数
        double m_dARMAX = 0.0;
        double m_dOldNoise = 0.0;

        //トーマス・フェアリングモデル
        double m_dR1Rv = 0.489;
        double m_dXARv = 0.0;


        /// <summary> 演算データ（キャスト用） </summary>
        McTestDataGeneratorCalInfo m_KMdlInf = 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.AddCheckWarningData(this.GetID(), McHyHdFormulaSampleH13Define.TEST_DATAGENERATOR_MODEL_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.TEST_DATAGENERATOR_MODEL_KIND, "No send port.");
            }
            for (long lP = 0; lP < lOutputDataNum; lP++)
            {   // 出力側接続線数分チェックを行う
                if( McTranInfoDefine.SINGLE_CELL_SERIAL != csOutputCellData[lP].GetTranInfoKind() )
                {   // ポイント時系列情報ではない場合

                    // エラー
                    csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.TEST_DATAGENERATOR_MODEL_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>true :正常 , false:異常</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;
            // 引数で与えられたデータを　キャストしています。
            McTestDataGeneratorInitialData csInDt = csInitialData as McTestDataGeneratorInitialData;

            if (csInDt != null)
            {
                m_KMdlInf.m_dBMX0 = csInDt.BM_X0;
                
                m_KMdlInf.m_dARX0 = csInDt.AR_X0;

                m_KMdlInf.m_dARAMX0 = csInDt.ARMA_X0;

                m_KMdlInf.m_dARivX0 = csInDt.TF_X0;

                bRtn = true;
            }

            // AR 用　初期値計算
            m_dRx = Math.Sqrt(1.0 - m_dR1 * m_dR1);

            // ARAM　用　初期値計算
            double m_dOldNoise = this.GetNextGauss(1.0,0.0);

            // トーマス・フェアリングモデル 用　初期値計算
            m_dR1Rv = Math.Sqrt(1.0 - m_KMdlInf.m_dR1ARiv * m_KMdlInf.m_dR1ARiv);

            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)
        {
            if (m_KMdlInf.m_iKind == 1)
            {
                m_KMdlInf.m_dOutputData = this.GetNextRand(m_KMdlInf.m_dMax, m_KMdlInf.m_dMin);
            }
            else if (m_KMdlInf.m_iKind == 2)
            {
                m_KMdlInf.m_dOutputData = this.GetNextGauss(m_KMdlInf.m_dSigma, m_KMdlInf.m_dMean);
            }
            else if (m_KMdlInf.m_iKind == 3)
            {
                m_KMdlInf.m_dOutputData = this.GetNextBM();
            }
            else if (m_KMdlInf.m_iKind == 4)
            {
                m_KMdlInf.m_dOutputData = this.GetNextAR();
            }
            else if (m_KMdlInf.m_iKind == 5)
            {
                m_KMdlInf.m_dOutputData = this.GetNextARMA();
            }
            else if (m_KMdlInf.m_iKind == 6)
            {
                m_KMdlInf.m_dOutputData = this.GetNextARriver();
            }
            else if (m_KMdlInf.m_iKind == 7)
            {
                m_KMdlInf.m_dOutputData = m_KMdlInf.m_dConstCoeff;
            }

            if (m_KMdlInf.m_bMinusCutFlg == true)
            {
                if (m_KMdlInf.m_dOutputData < 0.0) { m_KMdlInf.m_dOutputData = 0.0; }
            }

            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)
        {
            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "DataFusion", "in");
            HySCellData[] csSndCellData = null;
            for (long lLp = 0; lLp < lOutputDataNum; lLp++)
            {   // 出力する伝送データ数分繰り返します。
                // To Do
                // 出力する伝送データの型式や設定値等をチェックし、それに合わせて、伝送データに出力値を設定します。
                csSndCellData = csOutputCellData[lLp].PrepareSendCellD1();

                csSndCellData[0].m_dData[0] = m_KMdlInf.m_dOutputData; // 計算値を設定
            }

            //McLog.DebugOut(GetSimulationTime(), GetID(), "MyModel", "DataFusion", "out");
            return 0;
        }


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

        /// <summary><para>method outline:</para>
        /// <para>プロパティ情報を設定する</para>
        /// </summary>
        /// <example>
        /// <para>usage:</para>
        /// <para>SetProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">セル型プロパティ情報</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool SetProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            bool bRtn = false;
            // 使用しやすいようにキャストしておく
            m_KMdlInf = (McTestDataGeneratorCalInfo)m_csCalInfo;

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

                m_KMdlInf.m_bMinusCutFlg = csPrptyInfo.MINUS_CUT;

                m_KMdlInf.m_iKind = csPrptyInfo.KIND;

                m_KMdlInf.m_dMax = csPrptyInfo.RND_MAX;
                m_KMdlInf.m_dMin = csPrptyInfo.RND_MIN;

                m_KMdlInf.m_dMean = csPrptyInfo.GAUSS_MEAN;
                m_KMdlInf.m_dSigma = csPrptyInfo.GAUSS_SIGMA;

                //m_csMyInf.m_dGaussX0 = csPrptyInfo.m_dGaussX0;

                //m_csMyInf.m_dARX0 = csPrptyInfo.m_dARX0;
                m_KMdlInf.m_dR1AR = csPrptyInfo.AR_R1;
                m_KMdlInf.m_dWAR = csPrptyInfo.AR_SCALE_W;
                m_KMdlInf.m_dYAR = csPrptyInfo.AR_SCALE_Y;

                //m_csMyInf.m_dARAMX0 = csPrptyInfo.m_dARAMX0;
                m_KMdlInf.m_dFaiARMA = csPrptyInfo.ARMA_FAI;
                m_KMdlInf.m_dTheataARMA = csPrptyInfo.ARMA_THEATA;
                m_KMdlInf.m_dWARMA = csPrptyInfo.ARMA_SCALE_W;
                m_KMdlInf.m_dYARMA = csPrptyInfo.ARMA_SCALE_Y;

                //m_csMyInf.m_dARivX0 = csPrptyInfo.m_dARivX0;
                m_KMdlInf.m_dR1ARiv = csPrptyInfo.TF_R1;
                m_KMdlInf.m_dSgx = csPrptyInfo.TF_SGX;
                m_KMdlInf.m_dAvx = csPrptyInfo.TF_AVX;
                m_KMdlInf.m_dAx = csPrptyInfo.TF_AX;
                m_KMdlInf.m_dWARv = csPrptyInfo.TM_SCALE_W;

                m_KMdlInf.m_dConstCoeff = csPrptyInfo.CONST_COEFF;

                bRtn = true;

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

        /// <summary><para>method outline:</para>
        /// <para>一様乱数を発生させる</para>
        /// </summary>
        /// <example>
        /// <para>usage:</para>
        /// <para>double dDt = GetNextRand(dMax,dMin)</para>
        /// </example>
        /// <param name="dMax">最大値（>最小値）</param>
        /// <param name="dMin">最小値（>=0）</param>
        /// <returns>発生させた乱数値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected double GetNextRand(double dMax, double dMin)
        {
            return (dMax-dMin)*m_csRnd.NextDouble()+dMin;
        }

        /// <summary><para>method outline:</para>
        /// <para>正規乱数を発生させる(Box-Muller法による)</para>
        /// </summary>
        /// <example>
        /// <para>usage:</para>
        /// <para>double dDt = GetNextGauss()</para>
        /// </example>
        /// <param name="dSigma">標準偏差</param>
        /// <param name="dMean">平均値</param>
        /// <returns>発生させた乱数値</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected double GetNextGauss(double dSigma, double dMean)
        {
            double dRtn = 0.0;
            if (m_bGaussSW == true)
            {
                m_dv1 = GetNextRand(1.0, 1.0E-12);
                m_dv2 = GetNextRand(1.0, 0.0) * Math.PI * 2;
                m_dBoxMuller1 = Math.Sqrt(-2.0 * Math.Log(m_dv1));
                m_dBoxMuller2 = Math.Sin(m_dv2);
                m_bGaussSW = false;
            }
            else
            {
                m_dBoxMuller2 = Math.Cos(m_dv2);
                m_bGaussSW = true;
            }
            dRtn = dSigma * (m_dBoxMuller1 * m_dBoxMuller2) + dMean;

            return dRtn;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected double GetNextBM()
        {
            // 平均 0.0、標準偏差 1.0 のガウス (「正規」) 分布
            m_dGaussX += this.GetNextGauss(1.0,0.0);
            return m_dGaussX;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected double GetNextAR()
        {
            m_dARX = m_dR1 * m_dARX + this.GetNextGauss(1.0, 0.0) * m_dRx;
            return m_dARX * m_KMdlInf.m_dWAR + m_KMdlInf.m_dYAR;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected double GetNextARMA()
        {
            double dNewNoise = this.GetNextGauss(1.0, 0.0);
            m_dARMAX = m_KMdlInf.m_dFaiARMA * m_dARMAX + dNewNoise - m_dOldNoise * m_KMdlInf.m_dTheataARMA;
            m_dOldNoise = dNewNoise;
            return m_dARMAX * m_KMdlInf.m_dWARMA + m_KMdlInf.m_dYARMA;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected double GetNextARriver()
        {
            m_dXARv = m_KMdlInf.m_dR1ARiv * m_dXARv + this.GetNextGauss(1.0, 0.0) * m_dR1Rv;
            double dHt = m_KMdlInf.m_dSgx * m_dXARv + m_KMdlInf.m_dAvx;
            double dXt = Math.Exp(dHt) + m_KMdlInf.m_dAx;
            return dXt * m_KMdlInf.m_dWARv;
        }
    }
    //
}
