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

namespace TERASOLUNA.Fw.Utlib.Logging
{
    /// <summary>
    /// ログの内容を保持するためのクラス。
    /// </summary>
    /// <remarks>
    /// 発生したログが正しいかどうかを検査するために、ログの内容を保持する。
    /// 以下の内容を保持する。
    /// <list type="bullet">
    /// <item>実行したテストメソッド。</item>
    /// <item>ログメッセージ。</item>
    /// <item>ログ出力の原因となった例外。</item>
    /// <item>ログレベル</item>
    /// </list>
    /// </remarks>
    internal class LogObject
    {
        /// <summary>
        /// 実行したテストメソッドです。
        /// </summary>
        /// <remarks>
        /// コンストラクタで初期化します。
        /// </remarks>
        private readonly MethodBase _calledMethod;

        /// <summary>
        /// 実行したテストメソッドで発生したログメッセージです。
        /// </summary>
        /// <remarks>
        /// コンストラクタで初期化します。
        /// </remarks>
        private readonly object _logMessage;

        /// <summary>
        /// 実行したテストメソッドでログ出力の原因となった例外です。
        /// </summary>
        /// <remarks>
        /// コンストラクタで初期化します。
        /// </remarks>
        private readonly Type _exceptionType;

        /// <summary>
        /// ログレベルです。
        /// </summary>
        /// <remarks>
        /// コンストラクタで初期化します。
        /// </remarks>
        private readonly LogLevel _logLevel;

        /// <summary>
        /// 実行したテストメソッドを取得します。
        /// </summary>
        public MethodBase CalledMethod
        {
            get
            {
                return _calledMethod;
            }
        }

        /// <summary>
        /// ログメッセージを取得します。
        /// </summary>
        public object LogMessage
        {
            get
            {
                return _logMessage;
            }
        }

        /// <summary>
        /// ログ出力の原因となった例外を取得します。
        /// </summary>
        public Type ExceptionType
        {
            get
            {
                return _exceptionType;
            }
        }

        /// <summary>
        /// ログレベルを取得します。
        /// </summary>
        public LogLevel LogLevel
        {
            get
            {
                return _logLevel;
            }
        }

        /// <summary>
        /// <see cref="UnitLogObject"/> クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="calledMethod">実行したテストメソッド。</param>
        /// <param name="logMessage">ログメッセージ。</param>
        /// <param name="_exception">ログ出力の原因となった例外。</param>
        /// <param name="logLevel">ログレベル。</param>
        /// <remarks>コンストラクタです。</remarks>
        /// <exception cref="ArgumentNullException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <paramref name="calledMethod"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="logMessage"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="logLevel"/> が null 参照です。
        /// </item>
        /// </exception>
        public LogObject(MethodBase calledMethod, object logMessage, Type exceptionType, LogLevel logLevel)
        {
            if (calledMethod == null)
            {
                throw new ArgumentNullException("calledMethod");
            }

            if (logMessage == null)
            {
                throw new ArgumentNullException("logMessage");
            }

            _calledMethod = calledMethod;
            _logMessage = logMessage;
            _exceptionType = exceptionType;
            _logLevel = logLevel;
        }

        /// <summary>
        /// オブジェクトが他のオブジェクトと等しいかどうかを示す。
        /// </summary>
        /// <param name="obj">比較対象の <see cref="object"/> 。</param>
        /// <returns>比較結果フラグ。</returns>
        public override bool Equals(object obj)
        {
            LogObject unitLogObject = obj as LogObject;

            // 型がUnitLogObjectではない
            if (unitLogObject == null)
            {
                return false;
            }

            // 実行しているテストメソッドが同じかどうかのチェック
            if (!_calledMethod.Equals(unitLogObject.CalledMethod))
            {
                return false;
            }

            // 発生したログメッセージが同じかどうかのチェック
            if(!_logMessage.Equals(unitLogObject.LogMessage))
            {
                return false;
            }

            // ログの原因となった例外が同じかどうかのチェック
            // 比較対象のオブジェクトの例外タイプが両方ともnull または、
            // 比較対象のオブジェクトの例外タイプが両方とも同じでなければ、falseを返却する。
            bool allNull = (_exceptionType == null) && (unitLogObject.ExceptionType == null);
            bool exceptionTypeAreSame = (_exceptionType != null) && (_exceptionType.Equals(unitLogObject.ExceptionType));
            if (!(allNull || exceptionTypeAreSame))
            {
                return false;
            }

            // ログレベルが同じかどうかのチェック
            if (!_logLevel.Equals(unitLogObject.LogLevel))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// このインスタンスの情報を文字列として取得する。
        /// </summary>
        /// <returns>インスタンス情報。</returns>
        public override string ToString()
        {
            StringBuilder buffer = new StringBuilder();
            buffer.Append("[");
            buffer.Append(LogLevel);
            if (LogLevel.ToString().Length == 4)
            {
                buffer.Append(" ");
            }
            buffer.Append("] ");

            if (ExceptionType != null)
            {
                buffer.Append("[exceptionType:");
                buffer.Append(ExceptionType);
                buffer.Append("] ");
            }

            buffer.Append(LogMessage);

            return buffer.ToString();
        }

        /// <summary>
        /// ハッシュコードを取得します。
        /// </summary>
        /// <returns>ハッシュコード。</returns>
        public override int GetHashCode()
        {
            return CalledMethod.ToString().Length 
                + LogMessage.ToString().Length 
                + ExceptionType.ToString().Length 
                + LogLevel.ToString().Length;
        }
    }
}
