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

namespace TERASOLUNA.Fw.Web.HttpModule
{
    /// <summary>
    /// 任意の Post ボタンが押された時に画面遷移を行う機能を提供します。
    /// </summary>
    /// <remarks>
    /// <para>web.config に &lt;httpModules&gt; 設定が必要です。</para>
    /// </remarks>
    public class TransitionListenerImpl : IHttpModule
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(TransitionListenerImpl));

        /// <summary>
        /// <see cref="TransitionListenerImpl"/> オブジェクトの作成時に初期化コードを呼び出します。 
        /// </summary>
        /// <param name="context">
        /// ASP.NET アプリケーション内のすべてのアプリケーションオブジェクトに共通のメソッド、
        /// プロパティ、およびイベントへのアクセスを提供する <see cref="HttpApplication"/> オブジェクト。
        /// </param>
        /// <remarks>
        /// <see cref="HttpApplication"/> オブジェクトの
        /// <see cref="HttpApplication.PostRequestHandlerExecute"/> イベントのイベントハンドラを登録します。
        /// </remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")]
        public virtual void Init(HttpApplication context)
        {
            context.PostRequestHandlerExecute += new EventHandler(Application_PostRequestHandlerExecute);
        }

        /// <summary>
        /// <see cref="TransitionListenerImpl"/> オブジェクトで使用されるリソース (メモリを除く) を解放します。 
        /// </summary>
        public virtual void Dispose()
        {
        }

        /// <summary>
        /// <see cref="HttpApplication.PostRequestHandlerExecute"/> イベントのイベントハンドラ。
        /// </summary>
        /// <param name="sender">イベントのソース。</param>
        /// <param name="e">イベントデータを格納している<see cref="EventArgs"/>。</param>
        /// <remarks>
        /// 設定ファイルから、現在のページと遷移名に対する遷移先の URL を取得し、画面遷移を行います。
        /// </remarks>
        /// <exception cref="ConfigurationErrorsException">
        /// 遷移先画面ID、または遷移先ページが取得できない場合。
        /// </exception>
        public virtual void Application_PostRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication application = (HttpApplication)sender;
            HttpContext context = application.Context;

            //遷移先ページIDの取得
            string nextPageID = WebUtils.GetPageID();
            if (nextPageID != null)
            {
                //遷移先ページのパス取得
                string nextPath = PageConfiguration.GetPath(nextPageID);
                if (nextPath == null)
                {
                    ConfigurationErrorsException exception = new ConfigurationErrorsException(string.Format(
                        Properties.Resources.E_PAGE_INFO_NOT_FOUND, nextPageID));
                    if (_log.IsErrorEnabled)
                    {
                        _log.Error(exception.Message, exception);
                    }
                    throw exception;
                }
                string nextUrl = this.GetNextUrl(nextPath);

                // 画面遷移することを示すログを出力する。
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format(Properties.Resources.D_TRANSIT_PAGE, nextPageID, nextPath, nextUrl));
                }
                //リダイレクト
                context.Response.Redirect(nextUrl);
            }
        }

        /// <summary>
        /// 遷移先のURLを取得します。
        /// </summary>
        /// <param name="nextPath">遷移先ページ。</param>
        /// <returns>遷移先のURL。</returns>
        /// <remarks><see cref="WebUtils.Transit(String)"/> メソッドでクエリ文字列を指定された場合、
        /// クエリ文字列を付与した URL を返却します。</remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings"), 
        System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")]
        protected virtual string GetNextUrl(string nextPath)
        {
            string appPath = HttpContext.Current.Request.ApplicationPath;
            string nextUrl = string.Empty;
            if (("/".Equals(appPath)))
            {
                nextUrl = nextPath;
            }
            else
            {
                nextUrl = appPath + nextPath;
            }

            Dictionary<string, string> queries = WebUtils.GetQueryParameters();
            if (queries != null)
            {   // クエリ文字列のキーと値を保持するディクショナリがあれば付与する。
                foreach (string key in queries.Keys)
                {
                    nextUrl = WebUtils.AppendParameter(nextUrl, key, queries[key]);
                }
            }
            return nextUrl;
        }
    }
}
