// 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.Collections;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Text;
using TERASOLUNA.Fw.Client.Configuration.View;
using TERASOLUNA.Fw.Client.Conversion;
using TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Client.Forms
{
    /// <summary>
    /// 画面遷移機能を提供するクラスです。
    /// </summary>
    /// <remarks>
    /// <see cref="IForwardable"/> 実装クラスに対して、データを受け渡すための
    /// 機能を提供します。
    /// </remarks>
    [ToolboxItem(false)]
    public abstract class ForwarderBase : Component
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(ForwarderBase));

        /// <summary>
        /// コンバート ID を保持します。
        /// </summary>
        /// <remarks>
        /// <see cref="IForwardable.ViewData"/> の内容を遷移元から遷移先へ変換、
        /// 遷移先から遷移元へ反映するためのデータセット変換情報を識別する ID です。
        /// </remarks>
        private string _convertId = null;

        /// <summary>
        /// 遷移先の画面 ID を保持します。
        /// </summary>
        /// <remarks>
        /// 遷移先の <see cref="IForwardable"/> 実装クラスの型を取得するための画面 ID です。
        /// </remarks>
        private string _viewId = null;

        /// <summary>
        /// 画面遷移を実行する元の画面を保持します。
        /// </summary>
        private IForwardable _forwardHost = null;


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

        /// <summary>
        /// 画面遷移を実行する元の画面を取得または設定します。
        /// </summary>
        /// <value>
        /// 画面遷移を実行する元の画面。
        /// </value>
        [Category("画面遷移"),
        Description("画面遷移を実行する元の画面を設定します")]
        public IForwardable ForwardHost
        {
            get
            {
                return _forwardHost;
            }

            set
            {
                _forwardHost = value;
            }

        }

        /// <summary>
        /// コンバート ID を取得または設定します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <see cref="IForwardable.ViewData"/> の内容を遷移元から遷移先へ変換、
        /// 遷移先から遷移元へ反映するためのデータセット変換情報を識別する ID です。
        /// </para>
        /// <para>
        /// null または空文字が設定された場合、変換は行われません。
        /// </para>
        /// </remarks>
        /// <value>
        /// コンバート ID 。
        /// </value>
        [Category("画面遷移"),
         Description("画面データセットを変換するためのコンバートIDを設定します。")]
        public string ConvertId
        {
            get
            {
                return _convertId;
            }
            set
            {
                _convertId = value;
            }
        }

        /// <summary>
        /// 遷移先の画面 ID を取得または設定します。
        /// </summary>
        /// <remarks>
        /// <para>
        /// 遷移先の <see cref="IForwardable"/> 実装クラスの型を取得するための画面 ID です。
        /// </para>
        /// </remarks>
        /// <value>
        /// 遷移先の画面 ID 。
        /// </value>
        [Category("画面遷移"),
         Description("遷移先を生成するための画面IDを設定します")]
        public string ViewId
        {
            get
            {
                return _viewId;
            }
            set
            {
                _viewId = value;
            }
        }

        /// <summary>
        /// 画面遷移を実行します。
        /// </summary>
        /// <remarks>
        /// <para>以下の手順で画面遷移処理を実行します。</para>
        /// <list type="number">
        /// <item>
        /// <description>
        /// 画面IDより遷移先の画面インスタンスを生成。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// 遷移元画面のインスタンスから遷移先画面のインスタンスへ保持する
        /// コレクションの内容をコピー。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// 遷移元画面のインスタンスから遷移先画面のインスタンスへ画面 
        /// <see cref="DataSet"/> をコンバート。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// 遷移先画面の <see cref="IForwardable.Init"/> メソッドを用いて初期化。
        /// </description>
        /// </item>
        /// </list>
        /// <para>遷移元画面が設定されていない場合、「画面のコレクションのコピー」、
        /// 「 <see cref="DataSet"/> のコンバート」は実行されません。</para>
        /// <para>データセット変換情報が設定されていない場合、「 <see cref="DataSet"/> のコンバート」
        /// は実行されません。</para>
        /// </remarks>
        /// <returns>
        /// 遷移先画面の初期化 (<see cref="IForwardable.Init"/>) が true を返却した場合、
        /// <see cref="ForwardResult.None"/> を返す。 false を返した場合、
        /// <see cref="ForwardResult.Abort"/> を返す。
        /// </returns>
        /// <exception cref="ConfigurationErrorsException">
        /// アプリケーション構成ファイル、または、画面遷移設定ファイルの内容が不正のため、
        /// <see cref="ViewConfiguration"/> 設定情報を取得できません。
        /// </exception>
        /// <exception cref="TerasolunaException">
        /// 以下のような場合に例外をスローします。
        /// <list type="bullet">
        /// <item>
        /// <description>
        /// <see cref="ForwarderBase.ViewId"/> プロパティが null 参照または空文字に設定されています。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IForwardable"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="ForwarderBase.ConvertId"/> が指定されていますが、画面遷移元の <see cref="IForwardable.ViewData"/> 、
        /// または、画面遷移先の <see cref="IForwardable.ViewData"/> が null 参照です。
        /// </description>
        /// </item>
        /// <item>
        /// <description>
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </description>
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="ConversionException">
        /// データセット変換中にエラーが発生しました。
        /// </exception>
        public abstract ForwardResult Execute();

        /// <summary>
        /// 遷移元の画面から遷移先の画面へ、画面の保持するコレクションの内容のコピーを行います。
        /// </summary>
        /// <remarks>
        /// 遷移元と遷移先のコレクションに同キーのエントリが存在した場合、
        /// 遷移元の値で上書きされます。
        /// </remarks>
        /// <param name="source">遷移元のコレクション。</param>
        /// <param name="dest">遷移先のコレクション。</param>
        protected virtual void CopyViewItems(IForwardable source, IForwardable dest)
        {
            IDictionary destItems = dest.Items;
            IDictionary sourceItems = source.Items;

            foreach (DictionaryEntry entry in sourceItems)
            {
                destItems.Add(entry.Key, entry.Value);
            }

            if(_log.IsTraceEnabled)
            {
                if (destItems.Count > 0)
                {
                    StringBuilder message = new StringBuilder();
                    message.AppendLine(string.Format(
                        Properties.Resources.T_FORM_COLLECTION_COPY, source.GetType(), dest.GetType()));
                    foreach (DictionaryEntry entry in destItems)
                    {
                        message.AppendLine(string.Format(
                            Properties.Resources.T_DICTIONARY_KEY_VALUE, entry.Key, entry.Value));
                    }
                    _log.Trace(message.ToString().Trim());
                }
                else
                {
                    _log.Trace(string.Format(
                        Properties.Resources.T_FORM_COLLECTION_NOT_COPY, source.GetType(), dest.GetType()));
                }
            }
        }

        /// <summary>
        /// 画面 <see cref="DataSet"/> の変換処理を行います。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <paramref name="source"/> から <paramref name="dest"/> へ、 
        /// <see cref="ConvertId"/> に基づき、変換処理を行います。
        /// </para>
        /// <para>
        /// <see cref="ConvertId"/> が null または空文字列の場合、変換処理は実行されません。
        /// </para>
        /// <para>
        /// 本メソッドの呼び出しの際、
        /// <paramref name="source"/> と <paramref name="dest"/> は、null でないことが前提となります。
        /// </para>
        /// </remarks>
        /// <param name="source">遷移元画面の <see cref="IForwardable.ViewData"/> 。</param>
        /// <param name="dest">遷移先画面の <see cref="IForwardable.ViewData"/> 。</param>
        /// <exception cref="TerasolunaException">
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </exception>
        /// <exception cref="ConversionException">
        /// データセット変換中にエラーが発生しました。
        /// </exception>
        protected virtual void ConvertViewData(DataSet source, DataSet dest)
        {
            if (!string.IsNullOrEmpty(_convertId))
            {
                IConverter converter = ConverterFactory.CreateConverter();
                converter.Convert(_convertId, source, dest);
            }
        }

        /// <summary>
        /// 画面 <see cref="DataSet"/> の反映処理を行います。
        /// </summary>
        /// <remarks>
        /// <para>
        /// <paramref name="source"/> に対し、 <paramref name="dest"/> から、 
        /// <see cref="ConvertId"/> に基づき、反映処理を行います。
        /// </para>
        /// <para>
        /// <see cref="ConvertId"/> が null または空文字列の場合、反映処理は実行されません。
        /// </para>
        /// <para>
        /// 本メソッドの呼び出しの際、 
        /// <paramref name="source"/> と <paramref name="dest"/> は、 null でないことが前提となります。
        /// </para>
        /// </remarks>
        /// <param name="source">遷移元画面の <see cref="IForwardable.ViewData"/> 。</param>
        /// <param name="dest">遷移先画面の <see cref="IForwardable.ViewData"/> 。</param>
        /// <exception cref="TerasolunaException">
        /// <see cref="IConverter"/> 実装クラスのインスタンスを生成できません。
        /// </exception>
        /// <exception cref="ConversionException">
        /// データセット変換中にエラーが発生しました。
        /// </exception>
        protected virtual void ReverseViewData(DataSet source, DataSet dest)
        {
            if (!string.IsNullOrEmpty(_convertId))
            {
                IConverter converter = ConverterFactory.CreateConverter();
                converter.Reverse(_convertId, source, dest);
            }
        }
    }
}