// lucene version compatibility level: 4.8.1
namespace Lucene.Net.Analysis.Cn.Smart
{
    /*
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You 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.
     */

    /// <summary>
    /// <see cref="SmartChineseAnalyzer"/> utility constants and methods
    /// <para/>
    /// @lucene.experimental
    /// </summary>
    public class Utility
    {
        public static readonly char[] STRING_CHAR_ARRAY = "未##串".ToCharArray();

        public static readonly char[] NUMBER_CHAR_ARRAY = "未##数".ToCharArray();

        public static readonly char[] START_CHAR_ARRAY = "始##始".ToCharArray();

        public static readonly char[] END_CHAR_ARRAY = "末##末".ToCharArray();

        /// <summary>
        /// Delimiters will be filtered to this character by <see cref="Hhmm.SegTokenFilter"/>
        /// </summary>
        public static readonly char[] COMMON_DELIMITER = new char[] { ',' };

        /// <summary>
        /// Space-like characters that need to be skipped: such as space, tab, newline, carriage return.
        /// </summary>
        public static readonly string SPACES = " 　\t\r\n";

        /// <summary>
        /// Maximum bigram frequency (used in the smoothing function).
        /// </summary>
        public static readonly int MAX_FREQUENCE = 2079997 + 80000;

        /// <summary>
        /// Compare two arrays starting at the specified offsets.
        /// </summary>
        /// <param name="larray">left array</param>
        /// <param name="lstartIndex">start offset into <paramref name="larray"/></param>
        /// <param name="rarray">right array</param>
        /// <param name="rstartIndex">start offset into <paramref name="rarray"/></param>
        /// <returns>0 if the arrays are equal，1 if <paramref name="larray"/> &gt; 
        /// <paramref name="rarray"/>, -1 if <paramref name="larray"/> &lt; <paramref name="rarray"/></returns>
        public static int CompareArray(char[] larray, int lstartIndex, char[] rarray,
            int rstartIndex)
        {

            if (larray == null)
            {
                if (rarray == null || rstartIndex >= rarray.Length)
                    return 0;
                else
                    return -1;
            }
            else
            {
                // larray != null
                if (rarray == null)
                {
                    if (lstartIndex >= larray.Length)
                        return 0;
                    else
                        return 1;
                }
            }

            int li = lstartIndex, ri = rstartIndex;
            while (li < larray.Length && ri < rarray.Length && larray[li] == rarray[ri])
            {
                li++;
                ri++;
            }
            if (li == larray.Length)
            {
                if (ri == rarray.Length)
                {
                    // Both arrays are equivalent, return 0.
                    return 0;
                }
                else
                {
                    // larray < rarray because larray has ended first.
                    return -1;
                }
            }
            else
            {
                // differing lengths
                if (ri == rarray.Length)
                {
                    // larray > rarray because rarray has ended first.
                    return 1;
                }
                else
                {
                    // determine by comparison
                    if (larray[li] > rarray[ri])
                        return 1;
                    else
                        return -1;
                }
            }
        }

        /// <summary>
        /// Compare two arrays, starting at the specified offsets, but treating <paramref name="shortArray"/> as a prefix to <paramref name="longArray"/>.
        /// As long as <paramref name="shortArray"/> is a prefix of <paramref name="longArray"/>, return 0.
        /// Otherwise, behave as <see cref="CompareArray(char[], int, char[], int)"/>.
        /// </summary>
        /// <param name="shortArray">prefix array</param>
        /// <param name="shortIndex">offset into <paramref name="shortArray"/></param>
        /// <param name="longArray">long array (word)</param>
        /// <param name="longIndex">offset into <paramref name="longArray"/></param>
        /// <returns>0 if <paramref name="shortArray"/> is a prefix of <paramref name="longArray"/>, 
        /// otherwise act as <see cref="CompareArray(char[], int, char[], int)"/>.</returns>
        public static int CompareArrayByPrefix(char[] shortArray, int shortIndex,
            char[] longArray, int longIndex)
        {

            // a null prefix is a prefix of longArray
            if (shortArray == null)
                return 0;
            else if (longArray == null)
                return (shortIndex < shortArray.Length) ? 1 : 0;

            int si = shortIndex, li = longIndex;
            while (si < shortArray.Length && li < longArray.Length
                && shortArray[si] == longArray[li])
            {
                si++;
                li++;
            }
            if (si == shortArray.Length)
            {
                // shortArray is a prefix of longArray
                return 0;
            }
            else
            {
                // shortArray > longArray because longArray ended first.
                if (li == longArray.Length)
                    return 1;
                else
                    // determine by comparison
                    return (shortArray[si] > longArray[li]) ? 1 : -1;
            }
        }

        /// <summary>
        /// Return the internal <see cref="CharType"/> constant of a given character. 
        /// </summary>
        /// <param name="ch">input character</param>
        /// <returns>Constant from <see cref="CharType"/> describing the character type.</returns>
        /// <seealso cref="CharType"/>
        public static CharType GetCharType(char ch)
        {
            // Most (but not all!) of these are Han Ideographic Characters
            if (ch >= 0x4E00 && ch <= 0x9FA5)
                return CharType.HANZI;
            if ((ch >= 0x0041 && ch <= 0x005A) || (ch >= 0x0061 && ch <= 0x007A))
                return CharType.LETTER;
            if (ch >= 0x0030 && ch <= 0x0039)
                return CharType.DIGIT;
            if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' || ch == '　')
                return CharType.SPACE_LIKE;
            // Punctuation Marks
            if ((ch >= 0x0021 && ch <= 0x00BB) || (ch >= 0x2010 && ch <= 0x2642)
                || (ch >= 0x3001 && ch <= 0x301E))
                return CharType.DELIMITER;

            // Full-Width range
            if ((ch >= 0xFF21 && ch <= 0xFF3A) || (ch >= 0xFF41 && ch <= 0xFF5A))
                return CharType.FULLWIDTH_LETTER;
            if (ch >= 0xFF10 && ch <= 0xFF19)
                return CharType.FULLWIDTH_DIGIT;
            if (ch >= 0xFE30 && ch <= 0xFF63)
                return CharType.DELIMITER;
            return CharType.OTHER;
        }
    }
}
