using System;
using System.IO;
using System.Threading;

namespace CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase
{
    /// <summary><para>class outline:</para>
    /// <para>ファイル入出力ラッパ</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2009/06/30][新規作成]</para>
    /// </remarks>
    public static class HySDBAFileIOWrapper
    {

        /// <summary>
        /// 自クラス名(ログ出力用)
        /// </summary>
        const string m_csMyClassName = "HySDBAFileIOWrapper";

        /// <summary>
        /// リトライ回数
        /// </summary>
        const int RETRY_COUNTER_MAX = 5;

        /// <summary>
        /// リトライ間隔
        /// </summary>
        static readonly TimeSpan RETRY_INTERVAL = new TimeSpan(0, 0, 0, 0, 200);    // 200ms

        /// <summary><para>method outline:</para>
        /// <para>File.Moveのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>FileMove(srcFileName, dstFileName)</para>
        /// </example>
        /// <param name="srcFileName">移動元ファイル名</param>
        /// <param name="dstFileName">移動先ファイル名</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public static void FileMove(string srcFileName, string dstFileName)
        {
            const string csMyMethodName = "FileMove";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    File.Move(srcFileName, dstFileName);
                    return;
                }
                catch (Exception ex)
                {
                    if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "Exception-Type", ex.GetType().FullName,
                            "SrcFileName", srcFileName,
                            "DstFileName", dstFileName);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>File.Replaceのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>FileReplace(srcFileName, dstFileName, dstBackupFileName)</para>
        /// </example>
        /// <param name="srcFileName">置換元ファイル名</param>
        /// <param name="dstFileName">置換先ファイル名</param>
        /// <param name="dstBackupFileName">置換先ファイルのバックアップファイル名</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static void FileReplace(string srcFileName, string dstFileName, string dstBackupFileName)
        {
            const string csMyMethodName = "FileReplace";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    File.Replace(srcFileName, dstFileName, dstBackupFileName);
                    return;
                }
                catch (Exception ex)
                {
                    if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "Exception-Type", ex.GetType().FullName,
                            "SrcFileName", srcFileName,
                            "DstFileName", dstFileName,
                            "DstBackupFileName", dstBackupFileName);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>File.Deleteのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>FileDelete(fileName)</para>
        /// </example>
        /// <param name="fileName">削除するファイル名</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static void FileDelete(string fileName)
        {
            const string csMyMethodName = "FileDelete";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    File.Delete(fileName);
                    return;
                }
                catch (Exception ex)
                {
                    if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "Exception-Type", ex.GetType().FullName,
                            "FileName", fileName);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>Directory.CreateDirectoryのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>CreateDirectory(csDirectoryPath)</para>
        /// </example>
        /// <param name="csDirectoryPath">ディレクトリパス</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static void CreateDirectory(string csDirectoryPath)
        {
            const string csMyMethodName = "CreateDataDirectory";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    Directory.CreateDirectory(csDirectoryPath);
                    return;
                }
                catch (Exception ex)
                {
                    if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "DataDirectoryPath", csDirectoryPath);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>Directory.Deleteのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>DirectoryDelete(csDirectoryPath)</para>
        /// </example>
        /// <param name="csDirectoryPath">ディレクトリパス</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <exception cref="System.IO.IOException">フォルダ削除失敗(空フォルダではなかった)</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static void DirectoryDelete(string csDirectoryPath)
        {
            DirectoryDelete(csDirectoryPath, false);
        }

        /// <summary><para>method outline:</para>
        /// <para>Directory.Deleteのラッパ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>DirectoryDelete(csDirectoryPath, recursive)</para>
        /// </example>
        /// <param name="csDirectoryPath">ディレクトリパス</param>
        /// <param name="recursive">内部のエントリごと削除する場合はtrue,それ以外はfalse</param>
        /// <returns>無し</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <exception cref="System.IO.IOException">フォルダ削除失敗(空フォルダではなかった)</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static void DirectoryDelete(string csDirectoryPath, bool recursive)
        {
            const string csMyMethodName = "FileDelete";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    Directory.Delete(csDirectoryPath, recursive);
                    return;
                }
                catch (Exception ex)
                {
                    // フォルダが空でないから発生した例外は再スロー
                    if ((ex is IOException) && !recursive)
                    {
                        throw;
                    }
                    else if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "Exception-Type", ex.GetType().FullName,
                            "DirectoryPath", csDirectoryPath);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>new FileStreamのWrapper</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>NewFileStream(fileName, fileMode, fileAccess)</para>
        /// </example>
        /// <param name="fileName">オープンするファイル名</param>
        /// <param name="fileMode">ファイルモード</param>
        /// <param name="fileAccess">ファイルアクセス</param>
        /// <returns>ファイルストリーム</returns>
        /// <exception cref="System.Exception">リトライタイムアウト</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        public static FileStream NewFileStream(string fileName, FileMode fileMode, FileAccess fileAccess)
        {
            const string csMyMethodName = "NewFileStream";
            int iRetryCounter;
            for (iRetryCounter = RETRY_COUNTER_MAX; iRetryCounter > 0; --iRetryCounter)
            {
                try
                {
                    return new FileStream(fileName, fileMode, fileAccess);
                }
                catch (Exception ex)
                {
                    if (IsRetryableException(ex))
                    {
                        HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "Exception; retry after sleeping",
                            "FileName", fileName,
                            "FileMode", fileMode,
                            "FileAccess", fileAccess);
                        Thread.Sleep(RETRY_INTERVAL);
                    }
                    else
                    {
                        throw;
                    }
                }
            }
            throw new Exception(csMyMethodName + ": retry timeout");
        }

        /// <summary><para>method outline:</para>
        /// <para>リトライ可能例外判定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = IsRetryableException(ex)</para>
        /// </example>
        /// <param name="ex">IO操作で補足した例外オブジェクト</param>
        /// <returns>true:リトライ可能、false:リトライ不可能</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>バックグラウンドプロセスによる想定外ファイルアクセス対策</para>
        /// </remarks>
        private static bool IsRetryableException(Exception ex)
        {
            if (ex is IOException ||                // I/Oエラー
                ex is UnauthorizedAccessException)  // アクセス許可例外
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
