// Copyright (c) 2008, NTT DATA Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Configuration;
using System.Text;
using System.Web.Configuration;
using TERASOLUNA.Fw.Common.Configuration;
using TERASOLUNA.Fw.Common.Logging;
using TERASOLUNA.Fw.Web.Configuration.ExceptionCode;
using TERASOLUNA.Fw.Web.Configuration.ExceptionTransition;
using TERASOLUNA.Fw.Web.Configuration.Page;
using TERASOLUNA.Fw.Web.Configuration.RequestController;
using TERASOLUNA.Fw.Web.Configuration.Sql;

namespace TERASOLUNA.Fw.Web.Configuration
{
    /// <summary>
    /// Web アプリケーションの構成ファイルから、TERASOLUNAが提供する設定ファイルの設定情報を取得します。
    /// </summary>
    /// <remarks>
    /// 以下の設定ファイルから設定情報を取得します。
    /// <para>①ページ設定ファイル</para>
    /// <para>②画面遷移設定ファイル</para>
    /// <para>③エラー画面遷移設定ファイル</para>
    /// <para>④SQL文設定ファイル</para>
    /// <para>⑤ビジネスロジック設定ファイル</para>
    /// <para>⑥リクエストコントローラ設定</para>
    /// <para>⑦例外コード設定ファイル</para>
    /// </remarks>
    public abstract class TerasolunaWebConfiguration : TerasolunaConfiguration
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(TerasolunaWebConfiguration));

        /// <summary>
        /// sqlConfiguration の要素名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "sqlConfiguration" です。
        /// </remarks>
        private static readonly string SQLCONFIGURAION_SECTION_NAME = "sqlConfiguration";

        /// <summary>
        /// SqlConfigurationSection を保持するオブジェクトです。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        private static SqlConfigurationSection _sqlConfigSection = null;

        /// <summary>
        /// pageConfiguration の要素名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "pageConfiguration" です。
        /// </remarks>
        private static readonly string PAGECONFIGURAION_SECTION_NAME = "pageConfiguration";

        /// <summary>
        /// PageConfigurationSection を保持するオブジェクトです。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        private static PageConfigurationSection _pageConfigSection = null;

        /// <summary>
        /// ExceptionTransitionConfiguration の要素名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "exceptionTransitionConfiguration" です。
        /// </remarks>
        private static readonly string EXCEPTION_TRANSITION_CONFIGURATION_NAME = "exceptionTransitionConfiguration";

        /// <summary>
        /// ExceptionTransitionConfigurationSection を保持するオブジェクトです。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        private static ExceptionTransitionConfigurationSection _exceptionTransitConfigSection = null;

        /// <summary>
        /// RequestControllerConfiguration の要素名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "requestControllerConfiguration" です。
        /// </remarks>
        private static readonly string REQUEST_CONTROLLER_CONFIGURATION_NAME = "requestControllerConfiguration";

        /// <summary>
        /// RequestControllerConfigurationSection を保持するオブジェクトです。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        private static RequestControllerConfigurationSection _requestControllerConfigSection = null;

        /// <summary>
        /// ExceptionCodeConfiguration の要素名です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "exceptionCodeConfiguration" です。
        /// </remarks>
        private static readonly string EXCEPTION_CODE_CONFIGURATION_NAME = "exceptionCodeConfiguration";

        /// <summary>
        /// ExceptionTransitionConfigurationSection を保持するオブジェクトです。
        /// </summary>
        /// <remarks>
        /// デフォルトの値は、 null です。
        /// </remarks>
        private static ExceptionCodeConfigurationSection _exceptionCodeConfigSection = null;

        /// <summary>
        /// ロック用のオブジェクトです。
        /// </summary>
        private static object _syncRoot = new Object();
 
        /// <summary>
        /// <see cref="SqlConfigurationSection"/> オブジェクトを取得します。
        /// </summary>
        /// <remarks>
        /// <seealso cref="WebConfigurationManager.GetSection(string)"/> メソッドを使用して、<see cref="SqlConfigurationSection"/> オブジェクトを取得します。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの外部設定ファイルマージ処理で基準とする要素の XPATH の設定情報が不正です。
        /// </item>
        /// <item>
        /// ファイルの読み込み失敗です。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH が XML スキーマ検証エラーです。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH に 外部設定ファイルマージ処理でマージする要素の XPATH をマージ後に、XML スキーマ検証エラーです。
        /// </item>
        /// </list>
        /// </exception>
        public static SqlConfigurationSection SqlConfigSection
        {
            get
            {
                if (_sqlConfigSection == null)
                {
                    lock (_syncRoot)
                    {
                        if (_sqlConfigSection == null)
                        {
                            object scsObject = WebConfigurationManager.GetSection(SQLCONFIGURAION_SECTION_NAME);
                            if (scsObject == null)
                            {
                                return null;
                            }

                            SqlConfigurationSection scs = scsObject as SqlConfigurationSection;
                            if(scs == null)
                            {
                                string message = string.Format(
                                    Properties.Resources.E_CONFIGURATION_INVALID_CAST, "SqlConfiguration", scsObject.GetType().FullName);
                                ConfigurationErrorsException exception = new ConfigurationErrorsException(message);
                                if (_log.IsErrorEnabled)
                                {
                                    _log.Error(message, exception);
                                }
                                throw exception;
                            }

                            scs.CreateSqlElements();
                            _sqlConfigSection = scs;
                            if (_log.IsTraceEnabled)
                            {
                                StringBuilder message = new StringBuilder();
                                message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_PARAMETER, "SqlConfiguration", "files"));
                                foreach (FileElement element in scs.Files)
                                {
                                    message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_KEY, "path", element.Path));
                                }
                                _log.Trace(message.ToString().Trim());
                            }
                        }
                    }
                }
                return _sqlConfigSection;
            }
        }

        /// <summary>
        /// PageConfigurationSection オブジェクトを取得します。
        /// </summary>
        /// <remarks>
        /// <seealso cref="WebConfigurationManager.GetSection(string)"/> メソッドを使用して、<see cref="PageConfigurationSection"/> オブジェクトを取得します。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの外部設定ファイルマージ処理で基準とする要素の XPATH の設定情報が不正です。
        /// </item>
        /// <item>
        /// ファイルの読み込み失敗です。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH が XML スキーマ検証エラーです。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH に 外部設定ファイルマージ処理でマージする要素の XPATH をマージ後に、XML スキーマ検証エラーです。
        /// </item>
        /// </list>
        /// </exception>
        public static PageConfigurationSection PageConfigSection
        {
            get
            {
                if (_pageConfigSection == null)
                {
                    lock (_syncRoot)
                    {
                        if (_pageConfigSection == null)
                        {
                            object pcsObject = WebConfigurationManager.GetSection(PAGECONFIGURAION_SECTION_NAME);
                            if (pcsObject == null)
                            {
                                return null;
                            }

                            PageConfigurationSection pcs = pcsObject as PageConfigurationSection;
                            if(pcs == null)
                            {
                                string message = string.Format(
                                    Properties.Resources.E_CONFIGURATION_INVALID_CAST, "PageConfiguration", pcsObject.GetType().FullName);
                                ConfigurationErrorsException exception = new ConfigurationErrorsException(message);
                                if (_log.IsErrorEnabled)
                                {
                                    _log.Error(message, exception);
                                }
                                throw exception;
                            }

                            pcs.CreatePageElements();
                            _pageConfigSection = pcs;
                            if (_log.IsTraceEnabled)
                            {
                                StringBuilder message = new StringBuilder();
                                message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_PARAMETER, "PageConfiguration", "files"));
                                foreach (FileElement element in pcs.Files)
                                {
                                    message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_KEY, "path", element.Path));
                                }
                                _log.Trace(message.ToString().Trim());
                            }
                        }
                    }
                }
                return _pageConfigSection;
            }
        }

        /// <summary>
        /// ExceptionTransitionConfigurationSection オブジェクトを取得します。
        /// </summary>
        /// <remarks>
        /// <seealso cref="WebConfigurationManager.GetSection(string)"/> メソッドを使用して、 <see cref="ExceptionTransitionConfigurationSection"/> オブジェクトを取得します。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの外部設定ファイルマージ処理で基準とする要素の XPATH の設定情報が不正です。
        /// </item>
        /// <item>
        /// ファイルの読み込み失敗です。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH が XML スキーマ検証エラーです。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH に 外部設定ファイルマージ処理でマージする要素の XPATH をマージ後に、XML スキーマ検証エラーです。
        /// </item>
        /// </list>
        /// </exception>
        public static ExceptionTransitionConfigurationSection ExceptionTransitionConfigSection
        {
            get
            {
                if (_exceptionTransitConfigSection == null)
                {
                    lock (_syncRoot)
                    {
                        if (_exceptionTransitConfigSection == null)
                        {
                            object etcsObject = WebConfigurationManager.GetSection(EXCEPTION_TRANSITION_CONFIGURATION_NAME);
                            if (etcsObject == null)
                            {
                                return null;
                            }

                            ExceptionTransitionConfigurationSection etcs = etcsObject as ExceptionTransitionConfigurationSection;
                            if(etcs == null)
                            {
                                string message = string.Format(
                                    Properties.Resources.E_CONFIGURATION_INVALID_CAST, "ExceptionTransitionConfiguration", etcsObject.GetType().FullName);
                                ConfigurationErrorsException exception = new ConfigurationErrorsException(message);
                                if (_log.IsErrorEnabled)
                                {
                                    _log.Error(message, exception);
                                }
                                throw exception;
                            }

                            etcs.CreateExceptionTransitionElements();
                            _exceptionTransitConfigSection = etcs;
                            if (_log.IsTraceEnabled)
                            {
                                StringBuilder message = new StringBuilder();
                                message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_PARAMETER, "ExceptionCodeConfiguration", "files"));
                                foreach (FileElement element in etcs.Files)
                                {
                                    message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_KEY, "path", element.Path));
                                }
                                _log.Trace(message.ToString().Trim());
                            }
                        }
                    }
                }
                return _exceptionTransitConfigSection;
            }
        }

        /// <summary>
        /// RequestControllerConfigurationSection オブジェクトを取得します。
        /// </summary>
        /// <remarks>
        /// <seealso cref="WebConfigurationManager.GetSection(string)"/> メソッドを使用して、<see cref="RequestControllerConfigurationSection"/> オブジェクトを取得します。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの外部設定ファイルマージ処理で基準とする要素の XPATH の設定情報が不正です。
        /// </item>
        /// <item>
        /// ファイルの読み込み失敗です。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH が XML スキーマ検証エラーです。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH に 外部設定ファイルマージ処理でマージする要素の XPATH をマージ後に、XML スキーマ検証エラーです。
        /// </item>
        /// </list>
        /// </exception>
        public static RequestControllerConfigurationSection RequestControllerConfigSection
        {
            get
            {
                if (_requestControllerConfigSection == null)
                {
                    lock (_syncRoot)
                    {
                        if (_requestControllerConfigSection == null)
                        {
                            object rccsObject = WebConfigurationManager.GetSection(REQUEST_CONTROLLER_CONFIGURATION_NAME);
                            if (rccsObject == null)
                            {
                                return null;
                            }

                            RequestControllerConfigurationSection rccs = rccsObject as RequestControllerConfigurationSection;
                            if(rccs == null)
                            {
                                string message = string.Format(
                                    Properties.Resources.E_CONFIGURATION_INVALID_CAST, "RequestControllerConfiguration", rccsObject.GetType().FullName);
                                ConfigurationErrorsException exception = new ConfigurationErrorsException(message);
                                if (_log.IsErrorEnabled)
                                {
                                    _log.Error(message, exception);
                                }
                                throw exception;
                            }

                            _requestControllerConfigSection = rccs;
                            if (_log.IsTraceEnabled)
                            {
                                StringBuilder message = new StringBuilder();
                                message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_PARAMETER, "RequestControllerConfiguration", "controllers"));
                                foreach (RequestControllerElement element in rccs.Controllers)
                                {
                                    message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_KEY, element.Name, element.Type));
                                }
                                _log.Trace(message.ToString().Trim());
                            }
                        }
                    }
                }
                return _requestControllerConfigSection;
            }
        }

        /// <summary>
        /// <see cref="ExceptionCodeConfigurationSection"/> オブジェクトを取得します。
        /// </summary>
        /// <remarks>
        /// <seealso cref="WebConfigurationManager.GetSection(string)"/> メソッドを使用して、<see cref="ExceptionCodeConfigurationSection"/> オブジェクトを取得します。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// <list type="bullet">
        /// <item>
        /// 構成ファイルの外部設定ファイルマージ処理で基準とする要素の XPATH の設定情報が不正です。
        /// </item>
        /// <item>
        /// ファイルの読み込み失敗です。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH が XML スキーマ検証エラーです。
        /// </item>
        /// <item>
        /// 外部設定ファイルマージ処理で基準とする要素の XPATH に 外部設定ファイルマージ処理でマージする要素の XPATH をマージ後に、XML スキーマ検証エラーです。
        /// </item>
        /// </list>
        /// </exception>
        public static ExceptionCodeConfigurationSection ExceptionCodeConfigSection
        {
            get
            {
                if (_exceptionCodeConfigSection == null)
                {
                    lock (_syncRoot)
                    {
                        if (_exceptionCodeConfigSection == null)
                        {
                            object eccsObject = WebConfigurationManager.GetSection(EXCEPTION_CODE_CONFIGURATION_NAME);
                            if (eccsObject == null)
                            {
                                return null;
                            }

                            ExceptionCodeConfigurationSection eccs = eccsObject as ExceptionCodeConfigurationSection;
                            if(eccs == null)
                            {
                                string message = string.Format(
                                    Properties.Resources.E_CONFIGURATION_INVALID_CAST, "ExceptionCodeConfiguration", eccsObject.GetType().FullName);
                                ConfigurationErrorsException exception = new ConfigurationErrorsException(message);
                                if (_log.IsErrorEnabled)
                                {
                                    _log.Error(message, exception);
                                }
                                throw exception;
                            }

                            eccs.CreateExceptionCodeElements();
                            _exceptionCodeConfigSection = eccs;
                            if (_log.IsTraceEnabled)
                            {
                                StringBuilder message = new StringBuilder();
                                message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_PARAMETER, "ExceptionCodeConfiguration", "files"));
                                foreach (FileElement element in eccs.Files)
                                {
                                    message.AppendLine(string.Format(Properties.Resources.T_CONFIGURATION_LOAD_KEY, "path", element.Path));
                                }
                                _log.Trace(message.ToString().Trim());
                            }
                        }
                    }
                }
                return _exceptionCodeConfigSection;
            }
        }

    }
}
