using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using CommonMP.HYSSOP.CoreImpl.HSTools;
using CommonMP.HYSSOP.CoreImpl.HSData;

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 class HySDBALockFile
    {
        /// <summary>
        /// ロックファイルパス
        /// </summary>
        /// <remarks>
        /// ロックを保持している状態の時のみ、nullでないパスが設定される。
        /// </remarks>
        private string m_csLockFilePath = null;

        /// <summary>
        /// ロック用排他オブジェクト
        /// </summary>
        /// <remarks>実際にファイルを作成するのではなく、Mutexオブジェクトを利用する</remarks>
        HySMutex m_csLockObject = null;

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

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

        /// <summary>
        /// ロックファイル拡張子
        /// </summary>
        /// <remarks>
        /// オリジナルのファイル名に付加する
        /// </remarks>
        public const string LOCK_FILE_EXTENSION = ".lck";

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

        /// <summary>
        /// ロックモード
        /// </summary>
        public enum LockMode
        {
            /// <summary>
            /// 読み取り専用
            /// </summary>
            FOR_READONLY,
            /// <summary>
            /// 更新可能性あり
            /// </summary>
            FOR_MODIFY
        }

        /// <summary><para>method outline:</para>
        /// <para>デフォルトコンストラクタ</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>HySDBALockFile csLockFile = new HySDBALockFile();</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>HySDBALockFile 生成されたクラスのインスタンス</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public HySDBALockFile()
        {
        }

        /// <summary><para>method outline:</para>
        /// <para>デストラクタ</para>
        /// </summary>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>念のため、解放する</para>
        /// </remarks>
        ~HySDBALockFile()
        {
            try
            {
                UnLock();
            }
            catch
            {
            }
        }

        /// <summary><para>method outline:</para>
        /// <para>ロックする</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = Lock(csTargetFilePath, iLockMode);</para>
        /// </example>
        /// <param name="csTargetFilePath">対象ファイルのパス。同じフォルダに拡張子.lckを付けたロックファイルを作成する</param>
        /// <param name="iLockMode">ロックモード。現在のところ、動作に変わりはない</param>
        /// <returns>true:成功、false:失敗</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>ロックファイルが存在する場合、規定の間隔と回数を用いてリトライを行う。</para>
        /// </remarks>
        public bool Lock(string csTargetFilePath, LockMode iLockMode)
        {
            const string csMyMethodName = "Lock";

            // ロックを保持していたら失敗にする
            if (m_csLockFilePath != null)
            {
                HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "this object has been locked!!");
                return false;
            }

            // ディレクトリが作成されていない場合、新規作成する
            string csDirectoryPath = Path.GetDirectoryName(csTargetFilePath);
            if (!string.IsNullOrEmpty(csDirectoryPath) && !Directory.Exists(csDirectoryPath))
            {
                try
                {
                    HySDBAFileIOWrapper.CreateDirectory(csDirectoryPath);
                }
                catch (Exception ex)
                {
                    HySDBALog.WriteOnline(m_csMyClassName, csMyMethodName, "exception at creating data directory",
                        "directorypath", csDirectoryPath,
                        "exception", ex);
                    return false;   // ディレクトリ作成失敗
                }
            }
            // ロックファイル名を作成する
            string csLockFilePath = csTargetFilePath + LOCK_FILE_EXTENSION;

            // ロックファイルパス名からMutex名を作成する
            string csMutexName = csLockFilePath;
            foreach (char cInvalidChar in Path.GetInvalidFileNameChars())
            {
                csMutexName = csMutexName.Replace(cInvalidChar, '-');
            }

            HySDBALog.WriteDebug(m_csMyClassName, csMyMethodName, "Get lock start");
            // 名前付きMutexを生成し、取得する
            HySMutex csMutex = new HySMutex(new HySString(csMutexName));
            csMutex.Wait();
            HySDBALog.WriteDebug(m_csMyClassName, csMyMethodName, "Get lock end");

            // 成功したらロックファイルパス名とともに保存する
            m_csLockFilePath = csLockFilePath;
            m_csLockObject = csMutex;

            return true;

        }

        /// <summary><para>method outline:</para>
        /// <para>解放する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>UnLock()</para>
        /// </example>
        /// <param name="">無し</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public void UnLock()
        {
            if (m_csLockObject != null)
            {
                m_csLockObject.Release();
                m_csLockObject = null;
            }
            if (m_csLockFilePath != null)
            {
                m_csLockFilePath = null;
            }
        }
    }
}
