// Copyright (c) 2008, NTT DATA Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Text.RegularExpressions;
using TERASOLUNA.Fw.Common.Logging;

namespace TERASOLUNA.Fw.Common.Validation
{
    /// <summary>
    /// Validation Application Block を利用した入力値検証クラスから用いられる、
    /// 文字種チェックを提供するユーティリティクラスです。
    /// </summary>
    public static class CaseCheckUtil
    {
        /// <summary>
        /// <see cref="ILog"/> 実装クラスのインスタンスです。
        /// </summary>
        /// <remarks>
        /// ログ出力に利用します。
        /// </remarks>
        private static ILog _log = LogFactory.GetLogger(typeof(CaseCheckUtil));

        /// <summary>
        /// 半角カナのチェックに使用する文字列です。
        /// </summary>
        /// <remarks>
        /// この定数の値は "ｱｲｳｴｵｧｨｩｪｫｶｷｸｹｺｻｼｽｾｿﾀﾁﾂｯﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖｬｭｮﾗﾘﾙﾚﾛﾜｦﾝﾟﾞｰ･､｡｢｣" です。
        /// </remarks>
        private static readonly string HANKAKU_KANA_LIST =
            "ｱｲｳｴｵｧｨｩｪｫｶｷｸｹｺｻｼｽｾｿﾀﾁﾂｯﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖｬｭｮﾗﾘﾙﾚﾛﾜｦﾝﾟﾞｰ･､｡｢｣";

        ///<summary>
        /// 全角カナのチェックに使用する文字列です。
        ///</summary>
        /// <remarks>
        /// この定数の値は "アイウヴエオァィゥェォカキクケコヵヶガギグゲゴサシスセソザジズゼゾタチツテトダヂヅデドナニヌネノハヒフヘホバビブベボパピプペポマミムメモヤユヨャュョラリルレロワヮヰヱヲッンー" です。
        /// </remarks>
        private static readonly string ZENKAKU_KANA_LIST =
            "アイウヴエオァィゥェォカキクケコヵヶガギグゲゴサシスセソ"
                + "ザジズゼゾタチツテトダヂヅデドナニヌネノハヒフヘホ"
                + "バビブベボパピプペポマミムメモヤユヨャュョラリルレロ"
                + "ワヮヰヱヲッンー";

        ///<summary>
        /// 半角英数文字列のチェックに使用する正規表現です。
        ///</summary>
        private static readonly Regex ALPHA_NUMERIC_REGEXP =
                new Regex(@"^([0-9]|[a-z]|[A-Z])*$");

        ///<summary>
        /// 半角英数大文字列のチェックに使用する正規表現です。
        ///</summary>
        private static readonly Regex ALPHA_UPPER_NUMERIC_REGEXP =
                new Regex(@"^([0-9]|[A-Z])*$");

        ///<summary>
        /// 数値文字列のチェックに使用する正規表現です。
        ///</summary>
        private static readonly Regex NUMERIC_STRING = new Regex("^([0-9])*$");

        /// <summary>
        /// UNICODE文字コード'&#165;u0000'から'&#165;u00ff'の
        /// 範囲内に存在する全角文字列です。
        /// </summary>
        private static readonly string ZENKAKU_BEGIN_U00_LIST =
            "＼¢£§¨¬°±´¶×÷";

        /// <summary>
        /// 指定された文字が半角カナ文字であることをチェックします。
        /// </summary>
        /// <param name="c">チェック対象の文字。</param>
        /// <returns>半角カナ文字であれば true 。そうでなければ false 。</returns>
        private static bool IsHankakuKanaChar(char c)
        {
            return HANKAKU_KANA_LIST.IndexOf(c) >= 0;
        }

        /// <summary>
        /// 指定された文字が半角文字であることをチェックします。
        /// </summary>
        /// <param name="c">チェック対象の文字。</param>
        /// <returns>半角文字であれば true 。そうでなければ false 。</returns>
        ///
        private static bool IsHankakuChar(char c)
        {
            return (c <= '\u00ff' && ZENKAKU_BEGIN_U00_LIST.IndexOf(c) < 0)
                    || IsHankakuKanaChar(c);
        }

        /// <summary>
        /// 指定された文字が全角文字であることをチェックします。
        /// </summary>
        /// <param name="c">チェック対象の文字。</param>
        /// <returns>全角文字であれば true 。そうでなければ false 。</returns>
        private static bool IsZenkakuChar(char c)
        {
            return !IsHankakuChar(c);
        }

        /// <summary>
        /// 指定された文字が全角カナ文字であることをチェックする。
        /// </summary>
        /// <param name="c">チェック対象の文字。</param>
        /// <returns>全角カナ文字であれば true 。そうでなければ false 。</returns>
        private static bool IsZenkakuKanaChar(char c)
        {
            return ZENKAKU_KANA_LIST.IndexOf(c) >= 0;
        }

        /// <summary>
        /// 検証値が半角英数文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks> 
        /// 半角英数文字とは、半角数字( 0 〜 9 )と半角英字( a 〜 z 、 A 〜 Z )を併せた文字集合です。
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が半角英数文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsAlphaNumericString(string value)
        {
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }
            return ALPHA_NUMERIC_REGEXP.IsMatch(value);
        }

        /// <summary>
        /// 検証値が半角英数大文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 半角英数大文字とは、半角数字( 0 〜 9 )と半角大文字の英字( A 〜 Z )を併せた文字集合です。
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が半角英数大文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsUpperAlphaNumericString(string value)
        {
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }
            return ALPHA_UPPER_NUMERIC_REGEXP.IsMatch(value);
        }

        /// <summary>
        /// 検証値が数値文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 数値文字とは、半角数字( 0 〜 9 )です。
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が数値文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsNumericString(string value)
        {
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }

            return NUMERIC_STRING.IsMatch(value);
        }

        /// <summary>
        /// 検証値が半角カナ文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 半角カナ文字とは、次に示す文字集合です。"ｱｲｳｴｵｧｨｩｪｫｶｷｸｹｺｻｼｽｾｿﾀﾁﾂｯﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎﾏﾐﾑﾒﾓﾔﾕﾖｬｭｮﾗﾘﾙﾚﾛﾜｦﾝﾟﾞｰ･､｡｢｣"
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が半角英数文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsHankakuKanaString(string value)
        {
            // 検証値がnullまたは空文字の時、true返却
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }

            char[] chars = value.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if (!IsHankakuKanaChar(chars[i]))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 検証値が半角文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 半角文字とは、"＼¢£§¨¬°±´¶×÷"を除く unicode の0から255番目まで(拡張 ASCII コードの範囲)の文字に半角カナを加えた文字集合です。
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が半角文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsHankakuString(string value)
        {
            // 検証値がnullまたは空文字の時、true返却
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }

            char[] chars = value.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if (!IsHankakuChar(chars[i]))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 検証値が全角文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 全角文字とは、半角文字(半角文字列チェックの定義に準じる)ではない文字集合です。
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が全角文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsZenkakuString(string value)
        {
            // 検証値がnullまたは空文字の時、true返却
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }

            char[] chars = value.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if (!IsZenkakuChar(chars[i]))
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 検証値が全角カナ文字のみで構成される文字列であることをチェックします。
        /// </summary>
        /// <remarks>
        /// 全角カナ文字とは、次に示す文字集合です。 
        /// <para>
        /// "アイウヴエオァィゥェォカキクケコヵヶガギグゲゴサシスセソザジズゼゾ
        /// タチツテトダヂヅデドナニヌネノハヒフヘホバビブベボパピプペポマミムメモヤユヨ
        /// ャュョラリルレロ"ワヮヰヱヲッンー"
        /// </para>
        /// </remarks>
        /// <param name="value">検証値。</param>
        /// <returns>検証値が全角カナ文字のみで構成される文字列、または空文字列の場合、<c>true</c> 。それ以外の場合、<c>false</c> 。</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="value"/> が null 参照です。
        /// </exception>
        public static bool IsZenkakuKanaString(string value)
        {
            // 検証値がnullまたは空文字の時、true返却
            if (value == null)
            {
                ArgumentNullException exception = new ArgumentNullException("value");
                if (_log.IsErrorEnabled)
                {
                    _log.Error(string.Format(
                        Properties.Resources.E_NULL_ARGUMENT, "value"), exception);
                }
                throw exception;
            }

            if (value.Length == 0)
            {
                return true;
            }

            char[] chars = value.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if (!IsZenkakuKanaChar(chars[i]))
                {
                    return false;
                }
            }
            return true;
        }
    }
}
