// 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.Collections;
using System.Collections.Generic;

namespace TERASOLUNA.Fw.Common.Logging.TraceSourceLog
{
    /// <summary>
    /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスを生成するファクトリクラスです。
    /// </summary>
    public class TraceSourceLogFactory : LogFactory
	{
        /// <summary>
        /// <see cref="TraceSourceLogger"/> クラスのコンストラクタに引数として与える
        /// トレースソースの名前の初期値です。
        /// </summary>
        private static readonly string DEFAULT_SOURCE_NAME = "Default";

        /// <summary>
        /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスをキャッシュする際の
        /// キーを生成するためのフォーマットです。
        /// </summary>
        private static readonly string KEY_FORMAT = "{0}:{1}";

        /// <summary>
        /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスのキャッシュです。
        /// </summary>
        private IDictionary<string, ITraceSourceLog> _loggers = new Dictionary<string, ITraceSourceLog>();

        /// <summary>
        /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスをキャッシュする際の
        /// キーを生成するためのフォーマットを取得します。
        /// </summary>
        /// <value>
        /// キーを作成するフォーマット。
        /// </value>
        /// <remarks>
        /// デフォルトの値は、"{0}:{1}" です。
        /// </remarks>
        protected virtual string KeyFormat
        {
            get 
            { 
                return KEY_FORMAT; 
            }
        }

        /// <summary>
        /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスのキャッシュを取得または設定します。
        /// </summary>
        /// <value>
        /// <see cref="ITraceSourceLog"/> 実装クラスのインスタンスのキャッシュ。
        /// </value>
        /// <remarks>
        /// デフォルトの値は、空の <see cref="IDictionary{String,ITraceSourceLog}"/> インスタンスです。
        /// </remarks>
        protected IDictionary<string, ITraceSourceLog> Loggers
        {
            get 
            {
                return _loggers; 
            }
        }

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

        /// <summary>
        /// <see cref="TraceSourceLogger"/> インスタンスを取得します。
        /// </summary>
        /// <param name="className">生成しようとする <see cref="ILog"/> 実装クラスの
        /// インスタンスを識別するための文字列。</param>
        /// <param name="args">0 個以上のログインスタンスを生成するために必要な対象オブジェクトを含んだ object 型の可変長引数。</param>
        /// <returns><see cref="ITraceSourceLog"/> 実装クラスのインスタンス。</returns>
        protected override ILog GetInstance(string className, params object[] args)
		{
            string sourceName = GetSourceName(args);

            // インスタンスをキャッシュする際のキー
            string key = string.Format(KeyFormat, className, sourceName);

            ITraceSourceLog logger = null;

            bool canGetValue = false;
            ICollection loggersCollection = (ICollection)_loggers;
            lock (loggersCollection.SyncRoot)
            {
                canGetValue = _loggers.TryGetValue(key, out logger);
            }

            if (!canGetValue)
            {
                logger = CreateTraceSourceLoggerByName(className, sourceName);

                lock (loggersCollection.SyncRoot)
                {
                    _loggers[key] = logger;
                }
            }
            return logger;
		}

        /// <summary>
        /// <see cref="TraceSourceLogger"/> インスタンスを取得します。
        /// </summary>
        /// <param name="classType">生成しようとする <see cref="ILog"/> 実装クラスの
        /// インスタンスを識別するためのクラスの型。</param>
        /// <param name="args">0 個以上のログインスタンスを生成するために必要な対象オブジェクトを含んだ object 型の可変長引数。</param>
        /// <returns><see cref="ITraceSourceLog"/> 実装クラスのインスタンス。</returns>
        protected override ILog GetInstance(Type classType, params object[] args)
        {
            string sourceName = GetSourceName(args);

            // インスタンスをキャッシュする際のキー
            string key = string.Format(KeyFormat, classType.FullName, sourceName);

            ITraceSourceLog logger = null;

            bool canGetValue = false;
            ICollection loggersCollection = (ICollection)_loggers;
            lock (loggersCollection.SyncRoot)
            {
                canGetValue = _loggers.TryGetValue(key, out logger);
            }

            if (!canGetValue)
            {
                logger = CreateTraceSourceLoggerByType(classType, sourceName);

                lock (loggersCollection.SyncRoot)
                {
                    _loggers[key] = logger;
                }
            }
            return logger;
        }

        /// <summary>
        /// ソース名を取得します。
        /// </summary>
        /// <param name="args">0 個以上のログインスタンスを生成するために必要な対象オブジェクトを含んだ object 型の可変長引数。</param>
        /// <returns>
        /// <see cref="TraceSourceLogger"/> クラスのコンストラクタに引数として与える
        /// トレースソースの名前。
        /// </returns>
        protected virtual string GetSourceName(params object[] args)
        {
            string sourceName = DEFAULT_SOURCE_NAME;
            if (args.Length != 0 && args[0] != null)
            {
                string arg0 = args[0].ToString();
                if (arg0.Length != 0)
                {
                    sourceName = arg0;
                }
            }

            return sourceName;
        }

        /// <summary>
        /// <see cref="TraceSourceLogger"/> インスタンスを生成します。
        /// </summary>
        /// <param name="className">生成するインスタンスの型文字列。</param>
        /// <param name="sourceName"><see cref="TraceSourceLogger"/> クラスのコンストラクタに引数として与える
        /// トレースソースの名前。</param>
        /// <returns><see cref="TraceSourceLogger"/> インスタンス。</returns>
        protected virtual ITraceSourceLog CreateTraceSourceLoggerByName(string className, string sourceName)
        {
            ITraceSourceLog result = new TraceSourceLogger(className, sourceName);
            return result;
        }

        /// <summary>
        /// <see cref="TraceSourceLogger"/> インスタンスを生成します。
        /// </summary>
        /// <param name="classType">生成するインスタンスの型文字列。</param>
        /// <param name="sourceName"><see cref="TraceSourceLogger"/> クラスのコンストラクタに引数として与える
        /// トレースソースの名前。</param>
        /// <returns><see cref="TraceSourceLogger"/> インスタンス。</returns>
        protected virtual ITraceSourceLog CreateTraceSourceLoggerByType(Type classType, string sourceName)
        {
            ITraceSourceLog result = new TraceSourceLogger(classType, sourceName);
            return result;
        }

	}
}

