// 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 TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Client.Conversion
{
    /// <summary>
    /// <see cref="IConverter"/> 実装クラスのインスタンスを生成するメソッドを提供します。
    /// </summary>
    /// <remarks>
    /// <para>
    /// <see cref="CreateConverter"/> メソッドにより、アプリケーション構成ファイルの appSettings 要素から、
    /// <see cref="CONVERTER_TYPENAME"/> をキーにして、<see cref="IConverter"/> 実装クラスの型名を取得し、
    /// 対応するインスタンスを生成します。
    /// </para>
    /// <para>
    /// アプリケーション構成ファイルに設定が存在しない場合は、既定で <see cref="DataSetConverter"/> クラスの
    /// インスタンスを生成します。
    /// </para>
    /// </remarks>
    public class ConverterFactory
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(ConverterFactory));

        /// <summary>
        /// アプリケーション構成ファイルの appSettings 要素に指定される <see cref="IConverter"/> の key 名です。
        /// </summary>
        /// <remarks>
        /// <para>
        /// この定数の値は "ConverterTypeName" です。
        /// </para>
        /// </remarks>
        public static readonly string CONVERTER_TYPENAME = "ConverterTypeName";

        /// <summary>
        /// アプリケーション構成ファイルの appSettings 要素に指定されるファクトリ型を指定する
        /// キーです。
        /// </summary>
        /// <remarks>
        /// この定数の値は "ConverterFactoryTypeName" です。
        /// </remarks>
        public static readonly string CONVERTER_FACTORY_TYPENAME = "ConverterFactoryTypeName";

        /// <summary>
        /// ロック用のオブジェクトです。
        /// </summary>
        private static object _syncRoot = new Object();

        /// <summary>
        /// <see cref="ConverterFactory"/> のシングルトンインスタンスです。
        /// </summary>
        private static volatile ConverterFactory _factory = null;

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスに対応する <see cref="Type"/> インスタンスの
        /// キャッシュです。
        /// </summary>
        private Type _converterType = null;

        /// <summary>
        /// <see cref="ConverterFactory"/> を取得または設定します。
        /// </summary>
        /// <value><see cref="ConverterFactory"/> のシングルトンインスタンス。</value>
        protected static ConverterFactory Factory
        {
            get
            {
                if (_factory == null)
                {
                    lock (_syncRoot)
                    {
                        if (_factory == null)
                        {
                            _factory = ClassUtil.CreateInstanceFromAppSettings<ConverterFactory>(
                              CONVERTER_FACTORY_TYPENAME, typeof(ConverterFactory));
                        }
                    }
                }
                return _factory;
            }
        }

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスに対応する <see cref="Type"/> インスタンスの
        /// キャッシュを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="IConverter"/> 実装クラスに対応する <see cref="Type"/> インスタンスの
        /// キャッシュ。
        /// </value>
        protected Type ConverterType
        {
            get
            {
                return _converterType;
            }
            set
            {
                _converterType = value;
            }
        }

        /// <summary>
        /// <see cref="ConverterFactory"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <remarks>
        /// デフォルトコンストラクタです。
        /// </remarks>
        public ConverterFactory()
        {
        }

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成します。
        /// </summary>
        /// <returns><see cref="IConverter"/> 実装クラスのインスタンス。
        /// 生成に失敗した場合は、例外をスローし、 null は返しません。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </exception>
        /// <remarks>
        /// <para>
        /// このメソッドでは、アプリケーション構成ファイルの appSettings 要素から 
        /// <see cref="CONVERTER_TYPENAME"/> をキーにして、 <see cref="IConverter"/> 実装クラスの
        /// 型名を取得し、対応するインスタンスを生成します。
        /// </para>
        /// <para>
        /// アプリケーション構成ファイルに設定が存在しない場合は、既定で <see cref="DataSetConverter"/> クラスの
        /// インスタンスを生成します。
        /// </para>
        /// </remarks>
        /// <example>
        /// アプリケーション構成ファイルの設定例を以下に示します。
        /// <code>
        /// &lt;appSettings&gt;
        ///   &lt;add key="ConverterTypeName" value="TERASOLUNA.Sample.SampleConverter, TERASOLUNA.Sample"/&gt;
        /// &lt;/appSettings&gt;
        /// </code>
        /// </example>
        public static IConverter CreateConverter()
        {
            IConverter converter = Factory.CreateInstance();

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(Properties.Resources.D_CREATE_INSTANCE_SUCCESS,
                    typeof(IConverter).FullName, converter.GetType().FullName));
            }

            return converter;
        }

        /// <summary>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成します。
        /// </summary>
        /// <returns><see cref="IConverter"/> 実装クラスのインスタンス。
        /// 生成に失敗した場合は、例外をスローし、 null は返しません。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </exception>
        /// <remarks>
        /// <para>
        /// このメソッドでは、アプリケーション構成ファイルの appSettings 要素から 
        /// <see cref="CONVERTER_TYPENAME"/> をキーにして、 <see cref="IConverter"/> 実装クラスの
        /// 型名を取得し、対応するインスタンスを生成します。
        /// </para>
        /// <para>
        /// アプリケーション構成ファイルに設定が存在しない場合は、既定で <see cref="DataSetConverter"/> クラスの
        /// インスタンスを生成します。
        /// </para>
        /// </remarks>
        /// <example>
        /// アプリケーション構成ファイルの設定例を以下に示します。
        /// <code>
        /// &lt;appSettings&gt;
        ///   &lt;add key="ConverterTypeName" value="TERASOLUNA.Sample.SampleConverter, TERASOLUNA.Sample"/&gt;
        /// &lt;/appSettings&gt;
        /// </code>
        /// </example>
        protected virtual IConverter CreateInstance()
        {
            IConverter converter = null;
            if (_converterType == null)
            {
                // 型がキャッシュされていなければ設定ファイルから型名を取得して生成
                converter = 
                    ClassUtil.CreateInstanceFromAppSettings<IConverter>(CONVERTER_TYPENAME, typeof(DataSetConverter));
                
                // 生成したインスタンスの型をキャッシュ
                _converterType = converter.GetType();
            }
            else
            {
                // 型がキャッシュされていれば、その型のインスタンスを生成
                converter = ClassUtil.CreateInstanceByType<IConverter>(_converterType);
            }

            return converter;
        }
    }
}
