using System;
using System.Collections.Generic;

namespace Quadarax.Foundation.Core.Value
{
    public class StringPatternComparer:IEqualityComparer<string>
    {
        #region *** Constants ***

        public const char CS_DEFAULT_EXCLUSION = '/';
        public const char CS_DEFAULT_ANYCHARS = '*';
        public const char CS_DEFAULT_ALPHACHARS = '?';
        public const char CS_DEFAULT_NUMCHARS = '#';
        public const char CS_DEFAULT_ALPHANUMCHARS = '%';
        public const char CS_DEFAULT_SPACECHARS = '_';
        #endregion

        #region *** Fields ***
        private readonly char _exclusionChar;
        private readonly char _anyChars;
        private readonly char _alphaChars;
        private readonly char _numChars;
        private readonly char _spaceChars;
        private readonly char _alphanumChars;
        #endregion

        #region *** Constructors ***

        public StringPatternComparer(char exclusionChar = CS_DEFAULT_EXCLUSION, char anyChars = CS_DEFAULT_ANYCHARS,
            char alphaChars = CS_DEFAULT_ALPHACHARS, char numChars = CS_DEFAULT_NUMCHARS,
            char alphanumChars = CS_DEFAULT_ALPHANUMCHARS, char spaceChars = CS_DEFAULT_SPACECHARS)
        {
            _exclusionChar = exclusionChar;
            _anyChars = anyChars;
            _alphaChars = alphaChars;
            _alphanumChars = alphanumChars;
            _numChars = numChars;
            _spaceChars = spaceChars;
        }
        #endregion

        #region *** Public Operations ***

        /// <summary>
        /// Equals method for string pattern comparison. Patterns are:
        /// Exclusion char: /
        /// Any chars: * (0 - n)
        /// Any Alpha chars: ? (0 - n)
        /// Any numeric chars: # (0 - n)
        /// Any alphanumeric chars: % (0 - n)
        /// Any space chars: _ (0 - n)
        /// </summary>
        /// <param name="x">First string represents value to compare.</param>
        /// <param name="y">Second string represents search pattern.</param>
        /// <returns></returns>
        public bool Equals(string? x, string? y)
        {
            if (string.IsNullOrEmpty(x) && string.IsNullOrEmpty(y)) return true;
            if (string.IsNullOrEmpty(x) || string.IsNullOrEmpty(y)) return false;
            


            var curIndex = 0;
            var isExcluded = false;
            for (var i = 0; i < y.Length; i++)
            {
                if (y[i] == _exclusionChar)
                {
                    isExcluded = true;
                    continue;
                }

                curIndex = IndexOfPattern(x,curIndex, y[i], isExcluded);
                if (isExcluded)
                    isExcluded = false;

                if (curIndex == -2 || (curIndex == -1 && i == y.Length - 1))
                    return false;
            }
            return y.Length > 0;
        }


        public int GetHashCode(string obj)
        {
            return obj.GetHashCode(StringComparison.InvariantCulture);
        }
        #endregion
        #region *** Private Operations ***

        private int IndexOfPattern(string source, int startIndex, char patternChar, bool isExcluded)
        {
            if (startIndex >= source.Length)
                return -1;
            if (patternChar == _exclusionChar || patternChar == _anyChars)
                return startIndex;
            if (!(patternChar == _alphaChars || patternChar == _numChars || patternChar == _alphanumChars ||
                  patternChar == _spaceChars) || isExcluded)
                return source.IndexOf(patternChar, startIndex);


            for (var i = startIndex; i < source.Length; i++)
            {
                if ((patternChar == _alphaChars && char.IsLetter(source[i]))
                    || (patternChar == _numChars && char.IsNumber(source[i]))
                    || (patternChar == _alphanumChars && char.IsLetterOrDigit(source[i]))
                    || (patternChar == _spaceChars && char.IsWhiteSpace(source[i]))
                   )
                    return i;
            }

            return -2;

        }


        #endregion

    }
}
