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

namespace CommonMP.HYSSOP.CoreImpl.HSDBA.FileBase
{

    /// <summary><para>class outline:</para>
    /// <para>ファイルDBAレコードIDユーティリティクラス</para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.0.0][2009/10/07][新規作成]</para>
    /// </remarks>
    public class HySFileRecordIDUtility
    {
        ///<summary>IDエンコーディング</summary>
        private static readonly Encoding m_csIDEncoding = Encoding.UTF8;

        ///<summary>ID区切り文字(半角アンダースコア)</summary>
        private const char ID_DELIMITER = '_';

        ///<summary>IDエスケープシーケンス開始文字(半角パーセント)</summary>
        private const char ID_ESCAPE_START_CHAR = '%';

        ///<summary>空白文字(半角スペース)</summary>
        private const char ID_WHITE_SPACE_CHAR = ' ';

        ///<summary>ID予約文字配列</summary>
        private static readonly char[] ID_RESERVED_CHARS = new char[] {
            ID_DELIMITER,
            ID_ESCAPE_START_CHAR,
            ID_WHITE_SPACE_CHAR
        };

        /// <summary>ファイル名検索用のワイルドカード文字</summary>
        private const string WILDCARD_CHAR = "*";

        /// <summary><para>method outline:</para>
        /// <para>ID文字列を作成する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csIDString = HySFileRecordIDUtility.CreateIDString(csIDPartString) </para>
        /// </example>
        /// <param name="csIDPartString">ID構成部分文字列</param>
        /// <returns>ID文字列。予約文字、ファイル名に使えない文字およびマルチバイト文字はエスケープされる</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>任意個数の文字列を連結し、ファイル名に使える文字列を生成する。</para>
        /// <para><see cref="SplitIDString"/>で元のID構成部分文字列に分解できる。</para>
        /// </remarks>
        public static string CreateIDString(params string[] csIDPartString)
        {
            // ファイル名に使えない文字等をエスケープする
            string[] csEscapedStrings = new string[csIDPartString.Length];
            for (int idx = 0; idx < csIDPartString.Length; ++idx)
            {
                csEscapedStrings[idx] = EscapeIDPart(csIDPartString[idx]);
            }
            // 区切り文字で連結する
            string csIDString = string.Join(ID_DELIMITER.ToString(), csEscapedStrings);

            return csIDString;
        }

        /// <summary><para>method outline:</para>
        /// <para>ID文字列を作成する(非エスケープ版)</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csIDString = HySFileRecordIDUtility.CreateIDStringWithoutEscaping(csIDPartString) </para>
        /// </example>
        /// <param name="csIDPartString">ID構成部分文字列。エスケープされているものとする</param>
        /// <returns>ID文字列</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>任意個数の文字列を連結し、ファイル名に使える文字列を生成する。</para>
        /// </remarks>
        public static string CreateIDStringWithoutEscaping(params string[] csIDPartString)
        {
            // 区切り文字で連結する
            string csIDString = string.Join(ID_DELIMITER.ToString(), csIDPartString);

            return csIDString;
        }

        /// <summary><para>method outline:</para>
        /// <para>IDワイルドカードパターンを作成する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csPattern = HySFileRecordIDUtility.CreateIDWildCardPattern(csIDPartString) </para>
        /// </example>
        /// <param name="csIDPartString">ID構成部分文字列。空文字列を指定すると"*"に置き換える</param>
        /// <returns>ワイルドカードパターン文字列</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>任意個数の文字列を連結し、ファイル名検索に使える文字列を生成する。</para>
        /// </remarks>
        public static string CreateIDWildCardPattern(params string[] csIDPartString)
        {
            string[] csEscapedStrings = new string[csIDPartString.Length];
            for (int idx = 0; idx < csIDPartString.Length; ++idx)
            {
                if (!string.IsNullOrEmpty(csIDPartString[idx]))
                {
                    csEscapedStrings[idx] = EscapeIDPart(csIDPartString[idx]);
                }
                else
                {
                    csEscapedStrings[idx] = WILDCARD_CHAR;
                }
            }
            // 区切り文字で連結する
            string csIDString = string.Join(ID_DELIMITER.ToString(), csEscapedStrings);

            return csIDString;
        }

        /// <summary><para>method outline:</para>
        /// <para>ID文字列を分解する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string[] csString = HySFileRecordIDUtility.SplitIDString(csIDString) </para>
        /// </example>
        /// <param name="csIDString"><see cref="CreateIDString"/>で作成したID文字列</param>
        /// <returns>元の部分文字列。ただしエスケープされたままであることに注意</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>ID文字列を構成する</para>
        /// </remarks>
        public static string[] SplitIDString(string csIDString)
        {
            // ID区切り文字で分解した文字列配列を返す
            return csIDString.Split(ID_DELIMITER);
        }

        /// <summary><para>method outline:</para>
        /// <para>ID構成要素のエスケープ処理</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> string csString = EscapeIDPart(csIDPartString) </para>
        /// </example>
        /// <param name="csIDPartString">IDを構成する部分文字列</param>
        /// <returns>エスケープされた文字列</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static string EscapeIDPart(string csIDPartString)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in csIDPartString)
            {
                byte[] bytesOfC;
                if (IsReservedChar(c, out bytesOfC)
                    || IsInvalidFileNameChar(c, out bytesOfC)
                    || IsMultiByteChar(c, out bytesOfC))
                {
                    foreach (byte b in bytesOfC)
                    {
                        sb.Append(string.Format("{0}{1:X2}", ID_ESCAPE_START_CHAR, b));
                    }
                }
                else
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }

        /// <summary><para>method outline:</para>
        /// <para>予約文字判定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = IsReservedChar(cTest, bArray) </para>
        /// </example>
        /// <param name="cTest">調べる文字</param>
        /// <param name="bArray">cTestのバイト列。戻り値がfalseの時はnull</param>
        /// <returns>true: 予約文字、false: 予約文字ではない</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool IsReservedChar(char cTest, out byte[] bArray)
        {
            bArray = null;

            foreach (char cReserved in ID_RESERVED_CHARS)
            {
                if (cTest == cReserved)
                {
                    bArray = m_csIDEncoding.GetBytes(new char[] { cTest });
                    return true;
                }
            }
            return false;
        }

        /// <summary><para>method outline:</para>
        /// <para>ファイル名使用不可能文字判定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = IsInvalidFileNameChar(cTest, bArray) </para>
        /// </example>
        /// <param name="cTest">調べる文字</param>
        /// <param name="bArray">cTestのバイト列。戻り値がfalseの時はnull</param>
        /// <returns>true: 使用不可能、false: 使用可能</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool IsInvalidFileNameChar(char cTest, out byte[] bArray)
        {
            bArray = null;

            char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
            foreach (char ic in invalidFileNameChars)
            {
                if (cTest == ic)
                {
                    bArray = m_csIDEncoding.GetBytes(new char[] { cTest });
                    return true;
                }
            }
            return false;
        }

        /// <summary><para>method outline:</para>
        /// <para>マルチバイト文字判定</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para> bool bRtn = IsMultiByteChar(cTest, bArray) </para>
        /// </example>
        /// <param name="cTest">調べる文字</param>
        /// <param name="bArray">cTestのバイト列。戻り値がfalseの時はnull</param>
        /// <returns>true: マルチバイト文字、false: マルチバイト文字ではない</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        private static bool IsMultiByteChar(char cTest, out byte[] bArray)
        {
            byte[] bs = m_csIDEncoding.GetBytes(new char[] { cTest });
            if (bs.Length > 1)
            {
                bArray = bs;
                return true;
            }
            else
            {
                bArray = null;
                return false;
            }
        }
    }
}
