// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU Lesser General Public License(LGPL ver3).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace DotNetEx.IO
{
    //! @addtogroup DotNetEx-IO名前空間
    //! @{

    //! @brief パス解析ポリシーの読み取り専用インターフェースを提供します。
    public interface IPathPolicy
    {
        //! @brief 拡張子を区切る時に利用される文字列を取得します。
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> ExtentionSeparators { get; }
        //! @brief ディレクトリのセパレーターとしてみなす文字を取得します。
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> DirectorySeparators { get; }
        //! @brief 無効な文字を取得します。無効な文字は、例外を生じずに文字列から削除されるようになります。
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> InvalidChars { get; }
        //! @brief 大文字小文字を無視して比較するか取得します。
        bool? IsIgnoreCase { get; }
        //! @brief 末尾に存在する拡張子区切り文字列を削除するか取得します。
        bool? IsRemoveTrailExtentionSeparator { get; }
        //! @brief ルート要素から空白文字を削除するか取得します。
        bool? IsRemoveWhitespaceFromRoot { get; }
        //! @brief *.jpg.bmpのように複数の拡張子が出現する時、一番最初の拡張子から最後の文字までを全部拡張子としてみなすか取得します。falseの時は最後の一つだけ読み取ります。
        bool? IsMultiExtention { get; }
        //! @brief 複数パスを区切る時に利用される文字を取得します。
        System.String MultiPathSeparator { get; }
        //! @brief 相対パスを解決する時、カレントディレクトリを意味する名前(解決するときは単純に削除される名前)
        System.String RelativeCurrentDirectoryName { get; }
        //! @brief 相対パスを解決する時、親ディレクトリを意味する名前(解決すると一つ上のディレクトリが削除される名前)
        System.String RelativeParentDirectoryName { get; }
        //! @brief 文字列をルート（ドライブレター）とそれ以外に分割するための、グループ分けされた正規表現パターンを取得します。
        System.String RootParsePattern { get; }
        //! @brief RootParsePatternにおけるパスを決定するグループ名を取得します。
        System.String RootParsePatternPathGroupName { get; }
        //! @brief RootParsePatternにおけるルート（ドライブレター）を決定するグループ名を取得します。
        System.String RootParsePatternRootGroupName { get; }
    }
    
    //! @brief パス解析ポリシーを記述、設定するクラスです。
    //! @details
    public class PathPolicy : IPathPolicy
    {
        //! @brief 現在選択中のIPathPolicyを取得、設定します。
        //! @details IPathPolicyが必要なケースでIPathPolicyを省略した時にこのプロパティが参照されます。このメンバにnullを設定すると、代わりにDefaultが設定されます。
        public static IPathPolicy Current
        {
            get { return m_current; }
            set { m_current = value == null ? Default : value; }
        }

        //! @brief 規定値を取得します。
        public static readonly IPathPolicy Default;

        //! @brief 拡張子を区切る時に利用される文字を取得します。
        public System.Char[] ExtentionSeparators;
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> IPathPolicy.ExtentionSeparators
        {
            get { if (ExtentionSeparators == null) return null; return System.Array.AsReadOnly<System.Char>(ExtentionSeparators); }
        }

        //! @brief ディレクトリ区切りを指定します。規定値では{ System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar }です。
        //! @details このメンバを2文字以上に設定しておくと、文字列を代入する時（パスとして読む時）、先頭（0文字目）以外の文字は先頭文字に置換されます。
        public System.Char[] DirectorySeparators;
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> IPathPolicy.DirectorySeparators
        {
            get { if (DirectorySeparators == null) return null; return System.Array.AsReadOnly<System.Char>(DirectorySeparators); }
        }

        //! @brief 無効な文字のコレクションを指定します。
        //! @details 文字列を代入する時（パスとして読む時）、このメンバに含まれる文字は削除されます。また、この文字を1文字以上設定しておくと、
        //! パス区切り（１つの文字列を複数ファイルパスに分割/連結する時の区切り）が必要なケースで区切り文字の指定を省略する時のデフォルト区切り文字として読みます。
        //! このメンバの規定値は、System.IO.Path.GetInvalidPathChars()の返す配列です。
        public System.Char[] InvalidChars;
        System.Collections.ObjectModel.ReadOnlyCollection<System.Char> IPathPolicy.InvalidChars
        {
            get { if (InvalidChars == null) return null; return System.Array.AsReadOnly<System.Char>(InvalidChars); }
        }

        //! @brief 大文字小文字を無視するか取得,設定します。trueなら無視します。
        public bool? IsIgnoreCase;
        bool? IPathPolicy.IsIgnoreCase { get { return IsIgnoreCase; } }

        //! @brief 末尾に存在する拡張子区切り文字列を削除するか取得します。
        bool? IsRemoveTrailExtentionSeparator;
        bool? IPathPolicy.IsRemoveTrailExtentionSeparator { get { return IsRemoveTrailExtentionSeparator; } }

        //! @brief ルート要素から空白文字を削除するか取得します。
        public bool? IsRemoveWhitespaceFromRoot;
        bool? IPathPolicy.IsRemoveWhitespaceFromRoot { get { return IsRemoveWhitespaceFromRoot; } }

        //! @brief *.jpg.bmpのように複数の拡張子が出現する時、一番最初の拡張子から最後の文字までを全部拡張子としてみなすか取得します。falseの時は最後の一つだけ読み取ります。
        public bool? IsMultiExtention;
        bool? IPathPolicy.IsMultiExtention { get { return IsMultiExtention; } }

        //! @brief 複数パスを区切る時に利用される文字を取得します。
        public System.String MultiPathSeparator;
        System.String IPathPolicy.MultiPathSeparator { get { return MultiPathSeparator; } }

        //! @brief 相対パスを解決する時、カレントディレクトリを意味する名前(解決するときは単純に削除される名前)
        public System.String RelativeCurrentDirectoryName;
        System.String IPathPolicy.RelativeCurrentDirectoryName { get { return RelativeCurrentDirectoryName; } }

        //! @brief 相対パスを解決する時、親ディレクトリを意味する名前(解決すると一つ上のディレクトリが削除される名前)
        public System.String RelativeParentDirectoryName;
        System.String IPathPolicy.RelativeParentDirectoryName { get { return RelativeParentDirectoryName; } }

        //! @brief ドライブレターの解析パターンを返します。規定値では"^\s*(?<root>(.*\:)?(\/\\))(?<path>.*)"です。
        //! @details より正確には、コロンはSystem.IO.Path.VolumeSeparatorCharから、スラッシュとバックスラッシュはSystem.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorCharから取得されます。
        public System.String RootParsePattern;
        System.String IPathPolicy.RootParsePattern { get { return RootParsePattern; } }

        //! @brief RootParsePatternにおけるパスを決定するグループ名を取得します。
        public System.String RootParsePatternPathGroupName;
        System.String IPathPolicy.RootParsePatternPathGroupName { get { return RootParsePatternPathGroupName; } }
        
        //! @brief RootParsePatternにおけるルート（ドライブレター）を決定するグループ名を取得します。
        public System.String RootParsePatternRootGroupName;
        System.String IPathPolicy.RootParsePatternRootGroupName { get { return RootParsePatternRootGroupName; } }

        //! @brief 静的コンストラクタ
        static PathPolicy() { m_current = Default = new PathPolicy(); }

        //! @brief デフォルトコンストラクタ。標準設定でPathPolicyを新しく生成します。標準ではDefaultプロパティと同じ内容のデータになります。
        public PathPolicy()
        {
            ExtentionSeparators = new System.Char[] { '.' };
            System.Char[] invalidChars = System.IO.Path.GetInvalidPathChars();
            System.Collections.Generic.List<System.Char> invalidCodes = new System.Collections.Generic.List<char>(invalidChars.Length);
            foreach (System.Char ch in invalidChars)
            {
                if ((ch != System.IO.Path.AltDirectorySeparatorChar) &&
                    (ch != System.IO.Path.DirectorySeparatorChar) && (ch != ';') &&
                    (ch != System.IO.Path.VolumeSeparatorChar) && (ch != '.')) invalidCodes.Add(ch);
            }
            InvalidChars = new System.Char[invalidCodes.Count];
            invalidCodes.CopyTo(InvalidChars);
            IsIgnoreCase = true;
            IsMultiExtention = true;
            IsRemoveTrailExtentionSeparator = true;
            IsRemoveWhitespaceFromRoot = true;
            DirectorySeparators = new System.Char[] { System.IO.Path.AltDirectorySeparatorChar, System.IO.Path.DirectorySeparatorChar };
            MultiPathSeparator = ";";
            RelativeCurrentDirectoryName = ".";
            RelativeParentDirectoryName = "..";
            System.String EscapedSeparators = System.String.Empty;
            foreach (System.Char ch in DirectorySeparators) EscapedSeparators += "\\" + ch;
            RootParsePattern = System.String.Format("^\\s*(?<root>((.*{0}{1}[{2}\\s]*)|([{2}\\s]+))|)(?<path>.*)", '\\',
                System.IO.Path.VolumeSeparatorChar, EscapedSeparators);
            RootParsePatternPathGroupName = "path";
            RootParsePatternRootGroupName = "root";
        }

        //! @brief コンストラクタ
        //! @details IPathPolicyの値を元にクローニングしたPathPolicyを作成します。
        public PathPolicy(IPathPolicy src)
        {
            if (src.ExtentionSeparators != null)
            {
                ExtentionSeparators = new System.Char[src.ExtentionSeparators.Count];
                src.ExtentionSeparators.CopyTo(ExtentionSeparators, 0);
            }
            if (src.DirectorySeparators != null)
            {
                DirectorySeparators = new System.Char[src.DirectorySeparators.Count];
                src.DirectorySeparators.CopyTo(DirectorySeparators, 0);
            }
            if (src.InvalidChars != null)
            {
                InvalidChars = new System.Char[src.InvalidChars.Count];
                src.InvalidChars.CopyTo(InvalidChars, 0);
            }
            IsIgnoreCase = src.IsIgnoreCase;
            IsMultiExtention = src.IsMultiExtention;
            IsRemoveTrailExtentionSeparator = src.IsRemoveTrailExtentionSeparator;
            IsRemoveWhitespaceFromRoot = src.IsRemoveWhitespaceFromRoot;
            MultiPathSeparator = src.MultiPathSeparator;
            RelativeCurrentDirectoryName = src.RelativeCurrentDirectoryName;
            RelativeParentDirectoryName = src.RelativeParentDirectoryName;
            RootParsePattern = src.RootParsePattern;
            RootParsePatternPathGroupName = src.RootParsePatternPathGroupName;
            RootParsePatternRootGroupName = src.RootParsePatternRootGroupName;
        }

        private static IPathPolicy m_current;
    }

    //! @}
}
