// 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.Data;
using System.Reflection;
using System.Web;
using TERASOLUNA.Fw.Common;
using TERASOLUNA.Fw.Common.BLogic;
using TERASOLUNA.Fw.Common.Logging;
using TERASOLUNA.Fw.Web.BLogic;

namespace TERASOLUNA.Fw.Web.Controller
{
    /// <summary>
    /// クライアントからのマルチパート形式のアップロードを伴うビジネスロジック実行要求を解決するコントローラクラスです。
    /// </summary>
    public class MultipartUploadRequestController : BLogicRequestController
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(MultipartUploadRequestController));

        /// <summary>
        /// HTTP リクエストのヘッダ設定値を検証します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        protected override void ValidateRequestHeader(HttpContext context)
        {
        }

        /// <summary>
        /// HTTP リクエストのボディ部を解析します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// <list type="bullet">
        /// <item>
        /// <paramref name="context.RequestStream"/> が null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="context.Request.Headers"/> の キー値 <seealso cref="HttpContextWrapper.CONTENT_TYPE"/>  null 参照です。
        /// </item>
        /// <item>
        /// <paramref name="context.Request.Headers"/> の キー値 <seealso cref="HttpContextWrapper.CONTENT_TYPE"/> が空文字列です。
        /// </item>
        /// </list>
        /// </exception>
        /// <exception cref="InvalidRequestException">
        /// <list type="bullet">
        /// <item>
        /// ヘッダ情報の key が重複しています。
        /// </item>
        /// <item>
        /// ヘッダ情報の <seealso cref="MultipartUpload.CONTENT_TYPE"/> に <seealso cref="MultipartUpload.CONTENT_TYPE_VALUE"/> が存在しません。
        /// </item>
        /// <item>
        /// ヘッダ情報の <seealso cref="MultipartUpload.CONTENT_TYPE"/> に <seealso cref="MultipartUpload.BOUNDARY_KEY"/> が存在しません。
        /// </item>
        /// <item>
        /// ヘッダ情報の <seealso cref="MultipartUpload.CONTENT_TYPE"/> に <seealso cref="MultipartUpload.CHARSET_KEY"/> が存在しません。
        /// </item>
        /// <item>
        /// ボディ部に終端文字列が存在しません。
        /// </item>
        /// </list>
        /// </exception>
        protected override void ParseRequestBody(HttpContext context)
        {
            HttpContextWrapper wrapper = new HttpContextWrapper(context);
            MultipartUpload multipartUpload = new MultipartUpload(wrapper, new MultipartItemFactory());
            IDictionary<string, IMultipartItem> multipartList = multipartUpload.CreateMultipartItems();
            context.Items[KEY_CONTEXT_ITEM_INPUTPARAM] = multipartList;
            
        }

        /// <summary>
        /// 入力値の検証を行います。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <returns>
        /// 必ず true を返却します。
        /// </returns>
        protected override bool ValidateInputData(HttpContext context)
        {
            return true;
        }

        /// <summary>
        /// ビジネスロジックを実行します。
        /// </summary>
        /// <param name="context"> 
        /// HTTP 要求を処理するために使用する、組み込みのサーバー オブジェクト
        /// (たとえば、 Request 、 Response 、 Session 、および Server )
        /// への参照を提供する <see cref="HttpContext"/> クラスのインスタンス。
        /// </param>
        /// <returns>
        /// ビジネスロジックが正常終了した場合 true 、
        /// 業務エラーが発生した場合 false を返却します。
        /// </returns>
        /// <exception cref="TerasolunaException">
        /// <list type="bullet">
        /// <item>
        /// <see cref="IUploadBLogic"/> 実装クラスのコンストラクタが不正です。
        /// </item>
        /// <item>
        /// <see cref="IUploadBLogic"/> を実装していません。
        /// </item>
        /// <item>
        /// <see cref="IUploadBLogic"/> 実装クラスの結果オブジェクトが null 参照です。
        /// </item>
        /// </list>
        /// </exception>
        protected override bool ExecuteBLogic(HttpContext context)
        {
            string requestName = context.Items[RequestControllerConstants.KEY_REQUEST_NAME] as string;
            IBLogic blogic = BLogicFactory.CreateBLogic(requestName);

            IUploadBLogic uploadBLogic = blogic as IUploadBLogic;
            if (uploadBLogic == null)
            {
                // IUploadBLogicを実装していないクラスがリクエストコントローラとして設定されている場合。
                TerasolunaException exception = new TerasolunaException(string.Format(
                    Properties.Resources.E_NOT_IMPLEMENT, blogic.GetType().FullName, typeof(IUploadBLogic).FullName));
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            IDictionary<string, IMultipartItem> inputDictionary = context.Items[KEY_CONTEXT_ITEM_INPUTPARAM] as IDictionary<string, IMultipartItem>;
            uploadBLogic.MultipartItemList = inputDictionary;
            BLogicParam blogicParam = new BLogicParam();
            blogicParam.ParamData = new DataSet();

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_BEGIN_BLOGIC_FUNCTION, uploadBLogic.GetType().FullName));
            }

            BLogicResult result = uploadBLogic.Execute(blogicParam);

            if (_log.IsDebugEnabled)
            {
                _log.Debug(string.Format(
                    Properties.Resources.D_END_BLOGIC_FUNCTION, uploadBLogic.GetType().FullName));
            }

            if (result == null)
            {
                // 結果がnullの場合は電文を返せないので例外とする。
                TerasolunaException exception = new TerasolunaException(Properties.Resources.E_EMPTY_BLOGIC_RESULT);
                if (_log.IsErrorEnabled)
                {
                    _log.Error(exception.Message, exception);
                }
                throw exception;
            }

            context.Items[KEY_CONTEXT_ITEM_BLOGIC_RESULT] = result;

            if (result.Errors.Count > 0)
            {
                return false;
            }

            return true;
        }
    }
}
