//------------------------------------------------------------------------------
//      Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------

// Match is the result class for a regex search.
// It returns the location, length, and substring for
// the entire match as well as every captured group.
//

namespace System.Text.RegularExpressions
{

    using System.Collections;
    using System.Diagnostics;

    //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match"]/*' />
    /// <devdoc>
    ///    <para>
    ///       Represents 
    ///          the results from a single regular expression match.
    ///       </para>
    ///    </devdoc>
    //[ Serializable() ] 
    public class Match : Group {
        internal static Match _empty = new Match(null, 1, String.Empty, 0, 0, 0);
        internal GroupCollection _groupcoll;
        
        // input to the match
        internal Regex               _regex;
        internal int                 _textbeg;
        internal int                 _textpos;
        internal int                 _textend;
        internal int                 _textstart;

        // output from the match
        internal int[][]             _matches;
        internal int[]               _matchcount;
        internal bool                _balancing;

        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Empty"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns an empty Match object.
        ///    </para>
        /// </devdoc>
        public static Match Empty {
            get {
                return _empty;
            }
        }

        // Nonpublic constructor
        internal Match(Regex regex, int capcount, String text, int begpos, int len, int startpos)

        : base(text, new int[2], 0) {

            _regex      = regex;
            _matchcount = new int[capcount];

            _matches    = new int[capcount][];
            _matches[0] = _caps;
            _textbeg    = begpos;
            _textend    = begpos + len;
            _textstart  = startpos;
            _balancing  = false;

            // No need for an exception here.  This is only called internally, so we'll use an Assert instead
            //if (_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend)
            //    throw new ArgumentOutOfRangeException();

            System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend), 
                                            "The parameters are out of range.");
            
        }

        // Nonpublic set-text method
        internal virtual void Reset(Regex regex, String text, int textbeg, int textend, int textstart) {
            _regex = regex;
            _text = text;
            _textbeg = textbeg;
            _textend = textend;
            _textstart = textstart;

            for (int i = 0; i < _matchcount.Length; i++) {
                _matchcount[i] = 0;
            }

            _balancing = false;
        }

        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Groups"]/*' />
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public virtual GroupCollection Groups {
            get {
                if (_groupcoll == null)
                    _groupcoll = new GroupCollection(this, null);

                return _groupcoll;
            }
        }

        // Returns the next match
        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.NextMatch"]/*' />
        /// <devdoc>
        ///    <para>Returns a new Match with the results for the next match, starting
        ///       at the position at which the last match ended (at the character beyond the last
        ///       matched character).</para>
        /// </devdoc>
        public Match NextMatch() {
            if (_regex == null)
                return this;

            return _regex.Run(false, _length, _text, _textbeg, _textend - _textbeg, _textpos);
        }


        // Return the result string (using the replacement pattern)
        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Result"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns the expansion of the passed replacement pattern. For
        ///       example, if the replacement pattern is ?$1$2?, Result returns the concatenation
        ///       of Group(1).ToString() and Group(2).ToString().
        ///    </para>
        /// </devdoc>
        public virtual String Result(String replacement) {
            RegexReplacement repl;

            if (replacement == null)
                throw new ArgumentNullException("replacement");

            if (_regex == null)
                throw new NotSupportedException("No result on failed");//XXX: SR.GetString(SR.NoResultOnFailed));

            repl = (RegexReplacement)_regex.replref.Get();

            if (repl == null || !repl.Pattern.Equals(replacement)) {
                repl = RegexParser.ParseReplacement(replacement, _regex.caps, _regex.capsize, _regex.capnames, _regex.roptions);
                _regex.replref.Cache(repl);
            }

            return repl.Replacement(this);
        }

        // Used by the replacement code
        internal virtual String GroupToStringImpl(int groupnum) {
            int c = _matchcount[groupnum];
            if (c == 0)
                return String.Empty;

            int [] matches = _matches[groupnum];

            return _text.Substring(matches[(c - 1) * 2], matches[(c * 2) - 1]);
        }

        // Used by the replacement code
        internal String LastGroupToStringImpl() {
            return GroupToStringImpl(_matchcount.Length - 1);
        }


        // Convert to a thread-safe object by precomputing cache contents
        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Synchronized"]/*' />
        /// <devdoc>
        ///    <para>
        ///       Returns a Match instance equivalent to the one supplied that is safe to share
        ///       between multiple threads.
        ///    </para>
        /// </devdoc>
        static public Match Synchronized(Match inner) {
            if (inner == null)
                throw new ArgumentNullException("inner");

            int numgroups = inner._matchcount.Length;

            // Populate all groups by looking at each one
            for (int i = 0; i < numgroups; i++) {
                Group group = inner.Groups[i];

                // Depends on the fact that Group.Synchronized just
                // operates on and returns the same instance
                System.Text.RegularExpressions.Group.Synchronized(group);
            }

            return inner;
        }

        // Nonpublic builder: adds a group match by capnum
        internal virtual void AddMatch(int cap, int i, int l) {
            int capcount;

            if (_matches[cap] == null)
                _matches[cap] = new int[2];

            capcount = _matchcount[cap];

            if (capcount * 2 + 2 > _matches[cap].Length) {
                int[] oldmatches = _matches[cap];
                int[] newmatches = new int[capcount * 8];
                for (int j = 0; j < capcount * 2; j++)
                    newmatches[j] = oldmatches[j];
                _matches[cap] = newmatches;
            }

            _matches[cap][capcount * 2] = i;
            _matches[cap][capcount * 2 + 1] = l;
            _matchcount[cap] = capcount + 1;
        }

        // Nonpublic builder: adds a group match by capnum
        internal virtual void BalanceMatch(int cap) {
            int capcount;
            int target;

            _balancing = true;

            capcount = _matchcount[cap];

            target = capcount * 2 - 2;

            if (_matches[cap][target] < 0)
                target = -3 - _matches[cap][target];

            target -= 2;

            if (target >= 0 && _matches[cap][target] < 0)
                target = -3 - _matches[cap][target];

            AddMatch(cap, -3 - target, -4 - target);
        }

        // Nonpublic builder: removes a group match by capnum
        internal virtual void RemoveMatch(int cap) {
            _matchcount[cap]--;
        }

        // Nonpublic: tells if a group was matched by capnum
        internal virtual bool IsMatched(int cap) {
            return cap < _matchcount.Length && _matchcount[cap] > 0 && _matches[cap][_matchcount[cap] * 2 - 1] != (-3 + 1);
        }

        // Nonpublic: returns the index of the last specified matched group by capnum
        internal virtual int MatchIndex(int cap) {
            int i = _matches[cap][_matchcount[cap] * 2 - 2];
            if (i >= 0)
                return i;

            return _matches[cap][-3 - i];
        }

        // Nonpublic: returns the length of the last specified matched group by capnum
        internal virtual int MatchLength(int cap) {
            int i = _matches[cap][_matchcount[cap] * 2 - 1];
            if (i >= 0)
                return i;

            return _matches[cap][-3 - i];
        }

        //                                   
        //        
        

        // Nonpublic: tidy the match so that it can be used as an immutable result
        internal virtual void Tidy(int textpos) {
            int[] interval;

            interval  = _matches[0];
            _index    = interval[0];
            _length   = interval[1];
            _textpos  = textpos;
            _capcount = _matchcount[0];

            if (_balancing) {
                for (int cap = 0; cap < _matchcount.Length; cap++) {
                    int limit;
                    int[] matcharray;

                    limit = _matchcount[cap] * 2;
                    matcharray = _matches[cap];

                    int i = 0;
                    int j;

                    for (i = 0; i < limit; i++) {
                        if (matcharray[i] < 0)
                            break;
                    }

                    for (j = i; i < limit; i++) {
                        if (matcharray[i] < 0) {
                            j--;
                        }
                        else {
                            if (i != j)
                                matcharray[j] = matcharray[i];
                            j++;
                        }
                    }

                    _matchcount[cap] = j / 2;
                }

                _balancing = false;
            }
        }

#if DBG
        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Debug"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        public bool Debug {
            get {
                if (_regex == null)
                    return false;

                return _regex.Debug;
            }
        }

        //| <include file='doc\RegexMatch.uex' path='docs/doc[@for="Match.Dump"]/*' />
        /// <internalonly/>
        /// <devdoc>
        /// </devdoc>
        public virtual void Dump() {
            int i,j;

            for (i = 0; i < _matchcount.Length; i++) {
                System.Diagnostics.Debug.WriteLine("Capnum " + i.ToString() + ":");

                for (j = 0; j < _matchcount[i]; j++) {
                    String text = "";

                    if (_matches[i][j * 2] >= 0)
                        text = _text.Substring(_matches[i][j * 2], _matches[i][j * 2 + 1]);

                    System.Diagnostics.Debug.WriteLine("  (" + _matches[i][j * 2].ToString() + "," + _matches[i][j * 2 + 1].ToString() + ") " + text);
                }
            }
        }
#endif
    }


    // MatchSparse is for handling the case where slots are
    // sparsely arranged (e.g., if somebody says use slot 100000)
    internal class MatchSparse : Match {
        // the lookup hashtable
        new internal Hashtable _caps;

        // Nonpublic constructor
        internal MatchSparse(Regex regex, Hashtable caps, int capcount,
                             String text, int begpos, int len, int startpos)

        : base(regex, capcount, text, begpos, len, startpos) {

            _caps = caps;
        }

        public override GroupCollection Groups {
            get {
                if (_groupcoll == null)
                    _groupcoll = new GroupCollection(this, _caps);

                return _groupcoll;
            }
        }

#if DBG
        public override void Dump() {
            if (_caps != null) {
                IEnumerator e = _caps.Keys.GetEnumerator();

                while (e.MoveNext()) {
                    System.Diagnostics.Debug.WriteLine("Slot " + e.Current.ToString() + " -> " + _caps[e.Current].ToString());
                }
            }

            base.Dump();
        }
#endif

    }


}
