using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

using CommonMP.HYMCO.Interface;
using CommonMP.HYMCO.Interface.Model;
using CommonMP.HYMCO.Interface.Data;
using CommonMP.HYMCO.CoreImpl.Controller;
using CommonMP.HYMCO.CoreImpl.Data;
using CommonMP.HYMCO.CoreImpl.Tool;

using CommonMP.HYMCO.CoreOptionl.HymcoExpansionModel;

namespace CommonMP.HYMCO.Ocean.McOceanBaseModel
{
    /// <summary><para>class outline:</para>
    /// <para> 大気モデルクラス</para>
    /// 1.0×10 5Pa の O2 の水への溶解度は水1.0Lに対して、20℃で 1.3×10−3mol 
    /// 1 atm ＝ 1.01325×10 5 Pa
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[2021/12/31][新規作成]</para>
    public class MzOceanAtmosphere : MzOceanModelElement
    {
        /// <summary> 演算データ（キャスト用） </summary>
        MzOceanAtmosphereCalInfo m_OcLyrMdlInf = null; // 便利の為、キャスト用に定義しておく
        public MzOceanAtmosphereCalInfo GetOcnMdlCalInfo()
        {
            return m_OcLyrMdlInf;
        }

        //========================================================
        // CO2排出： McOceanBaseDefine.CO2_EMISSION
        protected McPatternRdvSndTranInfoPair m_csRcvCO2EmissionCnnct = new McPatternRdvSndTranInfoPair();
        protected McPatternRdvSndTranInfoPair m_csSndCO2DmyCnnct = new McPatternRdvSndTranInfoPair();

        // ガスの溶融・放出： McOceanBaseDefine.GASS_DISSOLVE
        protected McPatternRdvSndTranInfoPair m_csRcvDmyCnnct = new McPatternRdvSndTranInfoPair();
        protected McPatternRdvSndTranInfoPair m_csSndGassDissolveCnnct = new McPatternRdvSndTranInfoPair();

        // CO2調査
        protected McPatternRdvSndTranInfoPair m_csSndResearchCO2Cnnct = new McPatternRdvSndTranInfoPair();

        // --- work ----
        double[] m_dWorkO2Emission;
        double[] m_dWorkCO2Emission;
        
        //----------------------------------
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MzOceanAtmosphere()
        {

        }

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

        /// <summary><para>method outline:</para>
        /// <para>入力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = CheckReceiveDataConnection(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 CheckReceiveDataConnection(ref McStructErrorInfo csErrorInf, long lInputDataNum, McReceiveCellDataIF[] csInputCellData)
        {
            // チェック処理を入れる
            bool bRtn = base.CheckReceiveDataConnection(ref csErrorInf, lInputDataNum, csInputCellData);

            // CO2排出
            (m_csRcvCO2EmissionCnnct, m_csSndCO2DmyCnnct) = this.PrepareRcvSndPattern(MzOceanBaseDefine.CO2_EMISSION);

            // 海水への各種気体溶け込み
            (m_csRcvDmyCnnct, m_csSndGassDissolveCnnct) = this.PrepareRcvSndPattern(MzOceanBaseDefine.GASS_DISSOLVE);

            // CO2吸収量送信
            m_csSndResearchCO2Cnnct = this.PrepareSndPattern(MzOceanBaseDefine.RESEARCH_CO2_ABSORPTION);
           
            m_dWorkO2Emission = new double[m_csSndGassDissolveCnnct.m_RcvInfNum] ;
            m_dWorkCO2Emission = new double[m_csSndGassDissolveCnnct.m_RcvInfNum] ;

            // ディバッグ用送信データ
            //m_csDebugSendCnnct = this.PrepareSndPattern(MzOceanBaseDefine.DEBUG_OUT);
            return bRtn;
        }

        ///// <summary><para>method outline:</para>
        ///// <para>出力側の接続情報チェック</para>
        ///// </summary>
        ///// <example><para>usage:</para>
        ///// <para>bool bRtn = CheckSendDataConnection(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 CheckSendDataConnection(ref McStructErrorInfo csErrorInf, long lOutputDataNum, McSendCellDataIF[] csOutputCellData)
        //{
        //    // チェック処理を入れる
        //    bool bRtn = base.CheckSendDataConnection(ref csErrorInf, lOutputDataNum, csOutputCellData);
        //    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 = base.Initialize(ref csInitialData, lInputDataNum, ref csInputCellData);
            McInitialInfo csInDt = csInitialData as McInitialInfo;
            if (csInDt != null)
            {   // 初期化情報有り
                m_OcLyrMdlInf.m_Dissolve_O2intoSea = MzOceanAreaColumnModel.GetDisolveO2(20) * m_OcLyrMdlInf.m_dLayerLength[0];
                m_OcLyrMdlInf.m_Dissolve_CO2intoSea = MzOceanAreaColumnModel.GetDisolveCO2(20) * m_OcLyrMdlInf.m_dLayerLength[0];
            }
            return bRtn;
        }

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

        /// <summary>
        /// 受信
        /// </summary>
        protected override void ReceiveData()
        {
            //m_OcLyrMdlInf.m_ReqedO2 = 0;
            //m_OcLyrMdlInf.m_CO2Emission = 0;
            
            // CO2排出量を受信
            ReceiveCO2Emission();

            // 海からの排出を受信
            ReceiveDissolveGass();

            return;
        }

        /// <summary>
        /// 送信例
        /// </summary>
        protected override void SendData()
        {
            // O2消費量を送信
            SendO2Consumption();

            // 海への溶け込みを送信
            SendDissolveGass();

            // CO2調査
            SendResearchCO2();
            //// Debug
            SendDEBUG();
            return;
        }

        /// <summary>
        /// CO2排出：受信
        /// </summary>
        protected virtual void ReceiveCO2Emission()
        {
            m_OcLyrMdlInf.m_CO2Emission = 0;
            m_OcLyrMdlInf.m_ReqedO2 = 0;
            //------------------------
            // （大気側）                  
            for (int i = 0; i < m_csRcvCO2EmissionCnnct.m_RcvInfNum; i++)
            {
                HySCellData[] csCell = m_csRcvCO2EmissionCnnct.m_csRcvInfArry[i].GetInterpolatedCellD1();
                if (csCell != null)
                {
                    // CO2
                    m_OcLyrMdlInf.m_CO2Emission += csCell[0].m_dData[0];

                    // ReqO2
                    m_OcLyrMdlInf.m_ReqedO2 += csCell[0].m_dData[1] + 1.0E-16;
                    m_csRcvCO2EmissionCnnct.m_dRcvRatioData[i][0] = csCell[0].m_dData[1] + 1.0E-16;   // 分配の為、要求量を覚えておく
                }
            }
            for (int i = 0; i < m_csRcvCO2EmissionCnnct.m_RcvInfNum; i++)
            {
                m_csRcvCO2EmissionCnnct.m_dSendRatioData[i][0] = m_csRcvCO2EmissionCnnct.m_dRcvRatioData[i][0] / m_OcLyrMdlInf.m_ReqedO2;   // 分配の為、要求量を覚えておく
            }
        }

        /// <summary>
        /// O2消費量：送信送る
        /// </summary>
        protected virtual void SendO2Consumption()
        {
            HySCellData[] csSndDt = null;
            //送る側（ガス側）
            // O2量
            for (int i = 0; i < m_csRcvCO2EmissionCnnct.m_RcvInfNum; i++)
            {
                csSndDt = m_csRcvCO2EmissionCnnct.m_csSndInfArry[i].PrepareSendCellD1();
                if (csSndDt == null) { break; }
                csSndDt[0].m_dData[0] = m_OcLyrMdlInf.m_AvailableO2 * m_csRcvCO2EmissionCnnct.m_dSendRatioData[i][0];
            }
        }

        /// <summary>
        ///　海洋から大気への放出
        /// </summary>
        protected virtual void ReceiveDissolveGass()
        {
            m_OcLyrMdlInf.m_SurfaceO2FromSea = 0.0;
            m_OcLyrMdlInf.m_SurfaceCO2FromSea = 0;
            //m_OcLyrMdlInf.m_SeaO2 = 0;
            //m_OcLyrMdlInf.m_SeaO2 = 0;
            for (int i = 0; i < m_csSndGassDissolveCnnct.m_RcvInfNum; i++)
            {
                HySCellData[] csCell = m_csSndGassDissolveCnnct.m_csRcvInfArry[i].GetInterpolatedCellD1();
                if (csCell != null)
                {
                    // CO2
                    //m_dWorkO2Emission[i] = csCell[0].m_dData[0];
                    m_OcLyrMdlInf.m_SurfaceO2FromSea += csCell[0].m_dData[0];
                    m_OcLyrMdlInf.m_O2inSea = csCell[0].m_dData[2];
                    // CO2
                    //m_dWorkCO2Emission[i] = csCell[0].m_dData[1];
                    m_OcLyrMdlInf.m_SurfaceCO2FromSea += csCell[0].m_dData[1];
                    m_OcLyrMdlInf.m_CO2inSea = csCell[0].m_dData[3];
                }
            }
        }
        /// <summary>
        /// 大気から海洋への溶け込み
        /// </summary>
        protected virtual void SendDissolveGass()
        {
            if(m_csSndGassDissolveCnnct.m_RcvInfNum == 0) { return; }
            HySCellData[] csSndDt = null;
            //送る側（ガス側）
            // O2量
            double dK = 1.0 / m_csSndGassDissolveCnnct.m_RcvInfNum;
            for (int i = 0; i < m_csSndGassDissolveCnnct.m_RcvInfNum; i++)
            {
                csSndDt = m_csSndGassDissolveCnnct.m_csSndInfArry[i].PrepareSendCellD1();
                if (csSndDt == null) { break; }
                csSndDt[0].m_dData[0] = dK * m_OcLyrMdlInf.m_Dissolve_O2intoSea;
                csSndDt[0].m_dData[1] = dK * m_OcLyrMdlInf.m_Dissolve_CO2intoSea;
            }
        }

        /// <summary>
        /// 酸素、二酸化炭素吸収量の送信（調査用）
        /// </summary>
        protected virtual void SendResearchCO2()
        {
            for (int i = 0; i < m_csSndResearchCO2Cnnct.m_RcvInfNum; i++)
            {
                HySCellData[] csSndDt = m_csSndResearchCO2Cnnct.m_csSndInfArry[i].PrepareSendCellD1();
                if (csSndDt == null) { break; }

                csSndDt[0].m_dData[0] = m_OcLyrMdlInf.m_meanCO2Absorption;
                csSndDt[0].m_dData[1] = m_OcLyrMdlInf.m_meanCO2Emission;
                csSndDt[0].m_dData[2] = m_OcLyrMdlInf.m_meanO2Emission;
            }
        }

        /// <summary>
        /// ディバッグ出力
        /// </summary>
        protected virtual void SendDEBUG()
        {
            for (int i = 0; i < m_csDebugSendCnnct.m_RcvInfNum; i++)
            {
                HySCellData[] csCell = m_csDebugSendCnnct.m_csSndInfArry[i].PrepareSendCellD1();
                //for (int ll = 0; ll < m_OcLyrMdlInf.m_lLayerNumber; ll++)
                {
                    csCell[0].m_dData[0] = m_OcLyrMdlInf.m_meanO2Emission;
                    csCell[0].m_dData[1] = m_OcLyrMdlInf.m_meanCO2Emission;
                }
            }
        }


        /// <summary><para>method outline:</para>
        /// <para>モデル演算</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = Calculate(ref csInputDataList)</para>
        /// </example>
        /// <param name="lOnlyReceveDataNum">入力だけの情報数</param>
        /// <param name="csOnlyReceiveCellData">入力だけの情報配列</param>
        /// <param name="lOnlySendDataNum">出力だけの情報数</param>
        /// <param name="csOnlySelDataCellData">出力だけの情報配列</param>
        /// <param name="lRcvSndDataNum">相互接続報数</param>
        /// <param name="csReceiveCellData">相互接続入力側情報配列</param>
        /// <param name="csSelDataCellData">相互接続出力側情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>派生クラス側で必ずオーバライドする必要がある</para>
        /// </remarks>
        protected override long Calculate(
            long lOnlyReceveDataNum, ref McReceiveOnlyTranInfo[] csOnlyReceiveCellData,
            long lOnlySendDataNum, ref McSendOnlyTranInfo[] csOnlySelDataCellData,
            long lRcvSndDataNum, ref McRdvSndTranInfoPair[] csRcvSndCelDataCellData
            )
        {
            double dT = m_csDltTime.GetTime() / (3600.0 * 24.0);
            // 提供可能なO２量計算  m_AvailableO2
            // 　　O2は豊富にあり、要求量そのままが供給できるとする
            m_OcLyrMdlInf.m_AvailableO2 = m_OcLyrMdlInf.m_ReqedO2;

            // 海水へのガス溶け込み
            //   MzOceanDissolveGassCalInfo.S_O2: 海面上の酸素分圧、MzOceanDissolveGassCalInfo.S_CO2：海面上のCO2分圧　
            //   水中の分圧が高い時は放出する === m_OcLyrMdlInf.m_O2inSea  水中のO2圧、　 m_OcLyrMdlInf.m_CO2inSea  水中のCO2圧
            // ***大気圏から海水への吸収のみを計算する為、必ず＋とする。＝＝＞そのまま送信する。
            // ***海水から大気圏への放出は、McOceanDissolveGass（溶融ガス）側が計算して、その値を受信する。
            m_OcLyrMdlInf.m_Dissolve_O2intoSea = dT * (MzOceanAtmosphereCalInfo.S_O2 - m_OcLyrMdlInf.m_O2inSea);  // 溶融ガス量計算
            if (m_OcLyrMdlInf.m_Dissolve_O2intoSea < 0.0) { m_OcLyrMdlInf.m_Dissolve_O2intoSea = 0.0; }
            m_OcLyrMdlInf.m_Dissolve_CO2intoSea = dT * (MzOceanAtmosphereCalInfo.S_CO2 - m_OcLyrMdlInf.m_CO2inSea);  // 溶融ガス量計算
            if (m_OcLyrMdlInf.m_Dissolve_CO2intoSea < 0.0) { m_OcLyrMdlInf.m_Dissolve_CO2intoSea = 0.0; }

            // 海水層モデルのデータ
            MzOceanAreaColumnModelCalInfo csColumInfo = this.GetOceanAreaColumnCalInfo();

            // ★★★　集計用計算　★★★
            // 人間活動による(単位m2に換算する)
            m_OcLyrMdlInf.m_SumO2Emission -= m_OcLyrMdlInf.m_AvailableO2 / MzOceanModelElementCalInfo.m_dFace;
            m_OcLyrMdlInf.m_SumCO2Emission += m_OcLyrMdlInf.m_CO2Emission / MzOceanModelElementCalInfo.m_dFace;

            // ★★★　集計用計算　★★★
            // 海水からの放出
            m_OcLyrMdlInf.m_SumO2Emission += m_OcLyrMdlInf.m_SurfaceO2FromSea;
            m_OcLyrMdlInf.m_SumCO2Emission += m_OcLyrMdlInf.m_SurfaceCO2FromSea;

            // ★★★　集計用計算　★★★
            // 海水への溶け込み
            m_OcLyrMdlInf.m_SumO2Emission -= m_OcLyrMdlInf.m_Dissolve_O2intoSea;
            m_OcLyrMdlInf.m_SumCO2Emission -= m_OcLyrMdlInf.m_Dissolve_CO2intoSea;
            m_OcLyrMdlInf.m_SumCO2Absorption += m_OcLyrMdlInf.m_Dissolve_CO2intoSea;

            // ★★★　集計用計算　★★★
            // 酸素と二酸化炭素の一日の排出・吸収平均値を計算する
            m_OcLyrMdlInf.m_DaySec += m_csDltTime.TotalSec();
            if (m_OcLyrMdlInf.m_DaySec >= 24 * 3600)
            {

                m_OcLyrMdlInf.m_meanO2Emission = m_OcLyrMdlInf.m_SumO2Emission * (24 * 3600) / m_OcLyrMdlInf.m_DaySec;
                m_OcLyrMdlInf.m_meanCO2Emission = m_OcLyrMdlInf.m_SumCO2Emission * (24 * 3600) / m_OcLyrMdlInf.m_DaySec;
                m_OcLyrMdlInf.m_meanCO2Absorption = m_OcLyrMdlInf.m_SumCO2Absorption * (24 * 3600) / m_OcLyrMdlInf.m_DaySec;

                m_OcLyrMdlInf.m_SumO2Emission = m_OcLyrMdlInf.m_SumO2Emission * (m_OcLyrMdlInf.m_DaySec - 24 * 3600) / (24 * 3600);
                m_OcLyrMdlInf.m_SumCO2Emission = m_OcLyrMdlInf.m_SumCO2Emission * (m_OcLyrMdlInf.m_DaySec - 24 * 3600) / (24 * 3600);
                m_OcLyrMdlInf.m_SumCO2Absorption = m_OcLyrMdlInf.m_SumCO2Absorption * (m_OcLyrMdlInf.m_DaySec - 24 * 3600) / (24 * 3600);

                m_OcLyrMdlInf.m_DaySec -= 24 * 3600;
            }

            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)
        {
            base.SetProperty(csCellMdlPropertyInfo);

            // 使用しやすいようにキャストしておく
            m_OcLyrMdlInf = (MzOceanAtmosphereCalInfo)m_csCalInfo;

            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {

            }

            return true;
        }
 
    }
}
