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


namespace CommonMP.HYMCO.OptionImpl.ModelGeneratorSample
{
    /// <summary><para>class outline:</para>
    /// <para>モデルジェネレーター説明用演算モデル</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2010/01/01][新規作成]</para>
    /// <para>remarks:</para>
    /// <para>
    /// ユーザーが独自に作成したモデル要素。
    /// モデルジェネレーターサンプルでは、本モデル要素を組み合わせて
    /// 全体モデルを作成する例を作成する
    /// </para>
    /// </remarks>
    public class DmyBasinModel : McForecastModelBase
    {
        //=========================
        // 演算実行前処理関連メソッド
        //=========================
        DmyBasinCalInfo m_csMyDt = 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;

            for (long lP = 0; lP < lInputDataNum; lP++)
            {   // 入力側接続線数分チェックを行う
                long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                long lCellDim = csInputCellData[lP].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);
                if (lCellDim == 1 && lD1 == 1)
                {   // １次元配列で１セルならば
                    if (lCellDataNum < 1)
                    {   // セル内の変数の数
                       // ver1.5 エラートレース日本語対応
                        csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                            Properties.Resources.STATEMENT_VAL_NUM_SHORT_R + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                       // csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                       //     "Variable number in the Cells is too short. (Received from " + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                        bRtn = false;
                    }
                }
                else
                {
                    // エラー
                   // ver1.5 エラートレース日本語対応
                    csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                                            Properties.Resources.STATEMENT_UNEXPECT_DATA_TYPE_R + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                   // csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                   //                         "Unexpected send data type. (Received from " + csInputCellData[lP].GetUpperElementID().ToString() + ")");

                    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 != 1)
            //{
            //    csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
            //        "Send Connection number must be one.");
            //    bRtn = false;
            //}
            for (long lP = 0; lP < lOutputDataNum; lP++)
            {   // 出力側接続線数分チェックを行う
                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)
                    {   // セル内の変数の数
                       // ver1.5 エラートレース日本語対応
                        csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                            Properties.Resources.STATEMENT_VAL_NUM_SHORT_S  + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                       // csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                       //     "Variable number in the Cells is too short. (Send To " + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                        bRtn = false;
                    }
                }
                else
                {
                    // エラー
                   // ver1.5 エラートレース日本語対応
                    csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_KIND,
                                            Properties.Resources.STATEMENT_UNEXPECT_DATA_TYPE_S + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                   // csErrorInf.AddCheckErrorData(this.GetID(), SampleMdlGenDefine.TEST_DMYBASIN_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)
        {
            // 引数で与えられたデータを　キャストしています。
            McInitialInfo csInDt = csInitialData as McInitialInfo;

            if (csInDt != null)
            {

                m_csMyDt.m_dQ[0] = 100640.0/2.0;
                m_csMyDt.m_dQ[1] = 200770.0/2.0;
                m_csMyDt.m_dQ[2] = 718400.0/2.0;
                m_csMyDt.m_dQ[3] = 51840000.0/2.0;
                m_csMyDt.m_dQ[4] = 91840000.0/2.0;
                csInDt.GetInfo("m_dQ[0]", ref m_csMyDt.m_dQ[0]);
                csInDt.GetInfo("m_dQ[1]", ref m_csMyDt.m_dQ[1]);
                csInDt.GetInfo("m_dQ[2]", ref m_csMyDt.m_dQ[2]);
                csInDt.GetInfo("m_dQ[3]", ref m_csMyDt.m_dQ[3]);
                csInDt.GetInfo("m_dQ[4]", ref m_csMyDt.m_dQ[4]);

                m_csMyDt.m_dTotalOut = 0.0;

                this.Calculate(lInputDataNum, ref  csInputCellData);

                for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
                {
                    for (long lLp2 = 1; lLp2 < m_csMyDt.m_lDlyMx; lLp2++)
                    {
                        m_csMyDt.m_TransQ[lLp, m_csMyDt.m_lDlyMx - lLp2] = m_csMyDt.m_TransQ[lLp, 0];
                    }
                }
            }
            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)
        {
            double dTotalInQ = 0.0;

            HySCellData csCell = null;  // 取得するセル
            long lQIdx = 0;  // 流量は０番目
            // トータル流入を調べる
            for (long lP = 0; lP < lInputDataNum; lP++)
            {   // 
                csCell = csInputCellData[lP].GetInterpolatedCell(0); // セル配列の０番目のセルを取得
                dTotalInQ += csInputCellData[lP].Data(csCell, lQIdx);   // セル内の０番目の変数値を取得
            }
            dTotalInQ = dTotalInQ * m_csMyDt.m_dK * m_csMyDt.m_dArea / 1000.0; // 面積倍
            dTotalInQ = dTotalInQ * m_csDltTime.GetTime() / 3600.0; // δT秒累積　入力は時間単位であるため秒に変換するため ÷3600 している

            // チャージ
            double V = 1; //dTotalInQ * m_csMyDt.m_dOutResistanceOut;
            long lPNum = 1000;
            double[] dQ = new double[m_csMyDt.m_lStockNum];   // 流量 ｍ3/s

            double dDltQ = 2.0*dTotalInQ / (double)lPNum;
            for (int iLp = 0; iLp < lPNum; iLp++)
            {
                if (dTotalInQ <= 0.0) { break; }
                if (dTotalInQ < dDltQ) { dDltQ = dTotalInQ; }

                double dOutQ = 0.0001;
                for( long lLp=0;lLp<m_csMyDt.m_lStockNum;lLp++)
                {
                    dQ[lLp] = (V - (m_csMyDt.m_dQ[lLp] / m_csMyDt.m_dCapacitance[lLp])) / m_csMyDt.m_dResistanceIn[lLp];
                    if (dQ[lLp] < 0.0) { dQ[lLp] = 0.0; }
                    dOutQ += dQ[lLp];
                }
                double dTtlOutQ = 0.0;
                for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
                {
                    m_csMyDt.m_dQ[lLp] += dDltQ * (m_csDltTime.GetTime() / 3600.0) * dQ[lLp] / dOutQ;
                    dTtlOutQ += dDltQ * dQ[lLp] / dOutQ;
                }
                dTotalInQ -= dTtlOutQ;
                if (dTotalInQ <= 0.0001)
                { 
                    dTotalInQ = 0.0;
                    break;
                }
            }
            // ディスチャージ
            double dTotalDischage = 0.0;
            double[] dTmpQ = new double[m_csMyDt.m_lStockNum];   // 流量 ｍ3/s
            double[] dDltQOut = new double[m_csMyDt.m_lStockNum];   // 流量 ｍ3/s
            double[] dIndR = new double[m_csMyDt.m_lStockNum];   // 流量 ｍ3/s
            for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
            {
                //dTmpQ[lLp] = (m_csMyDt.m_dQ[lLp] / m_csMyDt.m_dCapacitance[lLp]) / m_csMyDt.m_dResistanceOut[lLp];
                //dDltQOut[lLp] = dTmpQ[lLp] - m_csMyDt.m_dPrevI[lLp];
                //dIndR[lLp] = dDltQOut[lLp] * m_csMyDt.m_dInductanceOut[lLp];
                dIndR[lLp] = m_csMyDt.m_dPrevDltQ[lLp] * m_csMyDt.m_dInductanceOut[lLp]; // 慣性項
                if (dTmpQ[lLp] < dIndR[lLp] * 0.05) { dIndR[lLp] = dTmpQ[lLp] * 0.95; }
                //if (dIndR[lLp] < 0.0) { dIndR[lLp] = 0.0; }
                if (dIndR[lLp] > 0.0) { dIndR[lLp] = 0.0; }
            }

            for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
            {
                if (m_csMyDt.m_dQ[lLp] < 0.0) {
                    m_csMyDt.m_dQ[lLp] = 0.0;
                }
                else
                {
                    dQ[lLp] = (m_csMyDt.m_dQ[lLp] / m_csMyDt.m_dCapacitance[lLp]) / m_csMyDt.m_dResistanceOut[lLp] - dIndR[lLp];
                }
                m_csMyDt.m_dPrevDltQ[lLp] = dQ[lLp] - m_csMyDt.m_dPrevI[lLp];
                m_csMyDt.m_dPrevI[lLp] = dQ[lLp];
                m_csMyDt.m_dQ[lLp] -= dQ[lLp] * (m_csDltTime.GetTime() / 3600.0);
                m_csMyDt.m_TransQ[lLp, 0] = dQ[lLp] * (m_csDltTime.GetTime() / 3600.0);
                //dTotalDischage += dQ[lLp];
                if (m_csMyDt.m_dQ[lLp] < 0.0) { m_csMyDt.m_dQ[lLp] = 0.0; }
            }
            for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
            {
                for (long lLp2 = 1; lLp2 < m_csMyDt.m_lDlyMx; lLp2++)
                {
                    m_csMyDt.m_TransQ[lLp, m_csMyDt.m_lDlyMx-lLp2] = m_csMyDt.m_TransQ[lLp, m_csMyDt.m_lDlyMx - lLp2 - 1];
                }
            }
            for (long lLp = 0; lLp < m_csMyDt.m_lStockNum; lLp++)
            {
                dTotalDischage += m_csMyDt.m_TransQ[lLp, m_csMyDt.m_lDlyTm[lLp]];
            }
            if (dTotalInQ > 0.0)
            {
                dTotalDischage += dTotalInQ;
            }
            m_csMyDt.m_dTotalOut = dTotalDischage;
            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[] csSndCellData1 = null;
            //HySCellData[,,] csSndCellData3 = null;


            for (long lLp = 0; lLp < lOutputDataNum; lLp++)
            {   // 出力する伝送データ数分繰り返します。
                csSndCellData1 = csOutputCellData[lLp].PrepareSendCellD1();
                csSndCellData1[0].m_dData[0] = m_csMyDt.m_dTotalOut;
            }
            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 = true;
            // 使用しやすいようにキャストしておく
            m_csMyDt = (DmyBasinCalInfo)m_csCalInfo;
            
            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                // 演算ステップ時刻設定
                this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());

                csPrptyInfo.GetInfo("m_dArea", ref m_csMyDt.m_dArea);
                csPrptyInfo.GetInfo("m_dCapacitance[0]", ref m_csMyDt.m_dCapacitance[0]);
                csPrptyInfo.GetInfo("m_dCapacitance[1]", ref m_csMyDt.m_dCapacitance[1]);
                csPrptyInfo.GetInfo("m_dCapacitance[2]", ref m_csMyDt.m_dCapacitance[2]);
                csPrptyInfo.GetInfo("m_dCapacitance[3]", ref m_csMyDt.m_dCapacitance[3]);
                csPrptyInfo.GetInfo("m_dCapacitance[4]", ref m_csMyDt.m_dCapacitance[4]);

                csPrptyInfo.GetInfo("m_dResistanceIn[0]", ref m_csMyDt.m_dResistanceIn[0]);
                csPrptyInfo.GetInfo("m_dResistanceIn[1]", ref m_csMyDt.m_dResistanceIn[1]);
                csPrptyInfo.GetInfo("m_dResistanceIn[2]", ref m_csMyDt.m_dResistanceIn[2]);
                csPrptyInfo.GetInfo("m_dResistanceIn[3]", ref m_csMyDt.m_dResistanceIn[3]);
                csPrptyInfo.GetInfo("m_dResistanceIn[4]", ref m_csMyDt.m_dResistanceIn[4]);

                csPrptyInfo.GetInfo("m_dResistanceOut[0]", ref m_csMyDt.m_dResistanceOut[0]);
                csPrptyInfo.GetInfo("m_dResistanceOut[1]", ref m_csMyDt.m_dResistanceOut[1]);
                csPrptyInfo.GetInfo("m_dResistanceOut[2]", ref m_csMyDt.m_dResistanceOut[2]);
                csPrptyInfo.GetInfo("m_dResistanceOut[3]", ref m_csMyDt.m_dResistanceOut[3]);
                csPrptyInfo.GetInfo("m_dResistanceOut[4]", ref m_csMyDt.m_dResistanceOut[4]);

                csPrptyInfo.GetInfo("m_lDlyTm[0]", ref m_csMyDt.m_lDlyTm[0]);
                csPrptyInfo.GetInfo("m_lDlyTm[1]", ref m_csMyDt.m_lDlyTm[1]);
                csPrptyInfo.GetInfo("m_lDlyTm[2]", ref m_csMyDt.m_lDlyTm[2]);
                csPrptyInfo.GetInfo("m_lDlyTm[3]", ref m_csMyDt.m_lDlyTm[3]);
                csPrptyInfo.GetInfo("m_lDlyTm[4]", ref m_csMyDt.m_lDlyTm[4]);
            }
            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()
        {
            // ToDo
            // 計算開始の　最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // メソッド自身を削除してください
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>計算中断時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SuspendCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>オペレーター操作等により計算中断時に　コールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool SuspendCalculation()
        {
            // ToDo
            // オペレーター操作等により計算中断時に　コールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // メソッド自身を削除してください
            return true;
        }
        /// <summary><para>method outline:</para>
        /// <para>計算終了時に動作する処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CompleteCalculation( )</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>true :正常 , false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>計算終了時　最後の最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。</para>
        /// </remarks>
        public override bool CompleteCalculation()
        {
            // ToDo
            // 計算終了時　最後の最初に１回だけコールされるメソッドです。不要ならば、本メソッドをオーバーライドする必要はありません。
            // メソッド自身を削除してください
            return true;
        }

        /// <summary><para>method outline:</para>
        /// <para>演算用刻み時間を自動的に変更する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>ChangeDeltaTimeAutomatically()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override void ChangeDeltaTimeAutomatically()
        {
            // ToDo
            // 演算中条件によって　自身のδTを(this.m_csDltTime) を変更するメソッドです。
            // 不要ならば、本メソッドをオーバーライドする必要はありません。
            // メソッド自身を削除してください
        }

    }
}

