using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Configuration;

namespace WSPOSInitiative.Common
{

    /// <summary>
    /// URIと一時ファイルのためのヘルパクラス
    /// The helper class for URI and temporary file 
    /// </summary>
    public class UriToTempFileHelper
    {
        public delegate void TargetMethod(string tempFileName);

        /// <summary>
        /// URIから一時ファイルにダウンロードしたあと指定のメソッドを実行し、
        /// メソッド終了後に一時ファイルを削除する
        /// After downloading from URI to temporary file, the appointed method is performed, 
        /// and temporary file is deleted after the end of a method. 
        /// </summary>
        public static void UriToTempFileExecuteTargetMethod(string uri, bool tempfileDelete, TargetMethod targetMethod)
        {
            try
            { 
                var req = WebRequest.Create(uri);

                try
                {
                    var res = req.GetResponse();

                    using (var resStream = res.GetResponseStream())
                    {
                        // テンポラリファイルの拡張子を指定されたファイルの拡張子と同じにする。
                        // ファイルを渡された際、拡張子によって内容判別のヒントにする場合があるため。
                        // It is made the same as the extension of the file which had the extension of a temporary file specified. 
                        // Since it may be made the hint of contents distinction by an extension when a file is passed. 
                        var extention = Path.GetExtension(new Uri(uri).AbsolutePath);
                        var tempFileNameWithTmp = GetTemporaryFileName();
                        var tempFileName = Path.ChangeExtension(tempFileNameWithTmp, extention);
                        try
                        {
                            using (var fileStream = new FileStream(tempFileName, FileMode.Create))
                            {
                                int size = 1024 * 1024;
                                byte[] buffer = new byte[size];
                                int numBytes;
                                while ((numBytes = resStream.Read(buffer, 0, size)) > 0)
                                {
                                    fileStream.Write(buffer, 0, numBytes);
                                }

                                //resStream.CopyTo(fileStream); There is't this method in .NET FW 3 
                            }
                            targetMethod(tempFileName);
                        }
                        finally
                        {
                            try
                            {
                                if (tempfileDelete)
                                {
                                    File.Delete(tempFileName);
                                }
                            }
                            catch { }
                        }
                    }
                }
                catch (DirectoryNotFoundException dnfex)
                {
                    throw new Microsoft.PointOfService.PosControlException(dnfex.Message, Microsoft.PointOfService.ErrorCode.NoExist, dnfex);
                }
                catch (FileNotFoundException fnfex)
                {
                    throw new Microsoft.PointOfService.PosControlException(fnfex.Message, Microsoft.PointOfService.ErrorCode.NoExist, fnfex);
                }
                catch (WebException webex)
                {
                    if (webex.Response == null)
                    {
                        for (Exception innerEx = webex.InnerException; innerEx != null; innerEx = innerEx.InnerException)
                        {
                            if (innerEx is FileNotFoundException || innerEx is DirectoryNotFoundException)
                            {
                                throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.NoExist, webex);
                            }
                        }
                        throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.Failure, webex);
                    }
                    if (webex.Response is HttpWebResponse)
                    {
                        var httpRes = webex.Response as HttpWebResponse;

                        if (httpRes.StatusCode == HttpStatusCode.Forbidden || httpRes.StatusCode == HttpStatusCode.NotFound || httpRes.StatusCode == HttpStatusCode.Gone)
                        {
                            // HTTP Status 403, 404, 410 --> map to  NOEXIST ERROR
                            throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.NoExist, webex);
                        }
                        else if ((int)httpRes.StatusCode >= 400)
                        {
                            // OTHER HTTP Status 4XX, 5XX --> map to  FAILURE ERROR
                            throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.Failure, webex);
                        }
                    }
                    else if (webex.Response is FtpWebResponse)
                    {
                        var ftpRes = webex.Response as FtpWebResponse;

                        if (ftpRes.StatusCode == FtpStatusCode.ActionNotTakenFileUnavailable || ftpRes.StatusCode == FtpStatusCode.ActionNotTakenFilenameNotAllowed)
                        {
                            // FTP Status 550, 553, --> map to NOEXIST ERROR
                            throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.NoExist, webex);
                        }
                        else if ((int)ftpRes.StatusCode >= 300)
                        {
                            // OTHER FTP Status 3XX, 4XX, 5XX --> map to FAILURE ERROR
                            throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.Failure, webex);
                        }
                    }
                    else
                    {
                        throw new Microsoft.PointOfService.PosControlException(webex.Message, Microsoft.PointOfService.ErrorCode.Failure, webex);
                    }
                }
                catch (Exception ex)
                {
                    throw new Microsoft.PointOfService.PosControlException(ex.Message, Microsoft.PointOfService.ErrorCode.Failure, ex);
                }
            }
            catch (Microsoft.PointOfService.PosControlException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new Microsoft.PointOfService.PosControlException(ex.Message, Microsoft.PointOfService.ErrorCode.Failure, ex);
            }
        }

        /// <summary>
        /// テンポラリファイルを作成し、メソッドを実行した後、テンポラリファイルをURIにアップロードする
        /// アップロード終了後にはテンポラリファイルを削除する。
        /// After creating a temporary file and performing a method, a temporary file is uploaded to URI. 
        /// Temporary file is deleted after the end of upload. 
        /// </summary>
        public static void ExecuteTargetMethodWithTempFileUpload(string uri, TargetMethod targetMethod)
        {
            var tempFileName = GetTemporaryFileName();

            try
            {
                targetMethod(tempFileName);

                using (var uploadClient = new WebClient())
                {
                    uploadClient.UploadFile(uri, tempFileName);
                }
            }
            finally
            {
                try
                {
                    File.Delete(tempFileName);
                }
                catch { }
            }
        }

        /// <summary>
        /// テンポラリファイル名の取得
        /// Acquisition of a temporary file name 
        /// </summary>
        public static string GetTemporaryFileName()
        {
            string temporaryFolder = GetTemporaryDirectory();

            var tempFileName = Path.Combine(temporaryFolder, Path.GetRandomFileName());
            
            return tempFileName;
        }

        /// <summary>
        /// テンポラリファイル用ディレクトリの取得
        /// Acquisition of the directory for temporary files 
        /// </summary>
        public static string GetTemporaryDirectory()
        {
            string temporaryFolder;

            if (StringArrayContainsHelper.Contains(ConfigurationManager.AppSettings.AllKeys, "CommonBehavior_TemporaryFileFolder"))
            {
                temporaryFolder = ConfigurationManager.AppSettings["CommonBehavior_TemporaryFileFolder"];
            }
            else
            {
                temporaryFolder = ".\\temp";
            }

            return temporaryFolder;
        }

    }
}
