/*
 * 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.
 */

using System;
using IndexReader = Lucene.Net.Index.IndexReader;
using Term = Lucene.Net.Index.Term;

namespace Lucene.Net.Search
{
	
	/// <summary> Subclass of FilteredTermEnum for enumerating all terms that match the
	/// specified wildcard filter term.
	/// <p>
	/// Term enumerations are always ordered by Term.compareTo().  Each term in
	/// the enumeration is greater than all that precede it.
	/// 
	/// </summary>
	/// <version>  $Id: WildcardTermEnum.java 329859 2005-10-31 17:05:36Z bmesser $
	/// </version>
	public class WildcardTermEnum : FilteredTermEnum
	{
		internal Term searchTerm;
		internal System.String field = "";
		internal System.String text = "";
		internal System.String pre = "";
		internal int preLen = 0;
		internal bool endEnum = false;
		
		/// <summary> Creates a new <code>WildcardTermEnum</code>.  Passing in a
		/// {@link Lucene.Net.index.Term Term} that does not contain a
		/// <code>WILDCARD_CHAR</code> will cause an exception to be thrown.
		/// <p>
		/// After calling the constructor the enumeration is already pointing to the first 
		/// valid term if such a term exists.
		/// </summary>
		public WildcardTermEnum(IndexReader reader, Term term):base()
		{
			searchTerm = term;
			field = searchTerm.Field();
			text = searchTerm.Text();
			
			int sidx = text.IndexOf((System.Char) WILDCARD_STRING);
			int cidx = text.IndexOf((System.Char) WILDCARD_CHAR);
			int idx = sidx;
			if (idx == - 1)
			{
				idx = cidx;
			}
			else if (cidx >= 0)
			{
				idx = System.Math.Min(idx, cidx);
			}
			
			pre = searchTerm.Text().Substring(0, (idx) - (0));
			preLen = pre.Length;
			text = text.Substring(preLen);
			SetEnum(reader.Terms(new Term(searchTerm.Field(), pre)));
		}
		
		protected internal override bool TermCompare(Term term)
		{
			if (field == term.Field())
			{
				System.String searchText = term.Text();
				if (searchText.StartsWith(pre))
				{
					return WildcardEquals(text, 0, searchText, preLen);
				}
			}
			endEnum = true;
			return false;
		}
		
		public override float Difference()
		{
			return 1.0f;
		}
		
		public override bool EndEnum()
		{
			return endEnum;
		}
		
		/// <summary>*****************************************
		/// String equality with support for wildcards
		/// ******************************************
		/// </summary>
		
		public const char WILDCARD_STRING = '*';
		public const char WILDCARD_CHAR = '?';
		
		/// <summary> Determines if a word matches a wildcard pattern.
		/// <small>Work released by Granta Design Ltd after originally being done on
		/// company time.</small>
		/// </summary>
		public static bool WildcardEquals(System.String pattern, int patternIdx, System.String string_Renamed, int stringIdx)
		{
			int p = patternIdx;
			
			for (int s = stringIdx; ; ++p, ++s)
			{
				// End of string yet?
				bool sEnd = (s >= string_Renamed.Length);
				// End of pattern yet?
				bool pEnd = (p >= pattern.Length);
				
				// If we're looking at the end of the string...
				if (sEnd)
				{
					// Assume the only thing left on the pattern is/are wildcards
					bool justWildcardsLeft = true;
					
					// Current wildcard position
					int wildcardSearchPos = p;
					// While we haven't found the end of the pattern,
					// and haven't encountered any non-wildcard characters
					while (wildcardSearchPos < pattern.Length && justWildcardsLeft)
					{
						// Check the character at the current position
						char wildchar = pattern[wildcardSearchPos];
						
						// If it's not a wildcard character, then there is more
						// pattern information after this/these wildcards.
						if (wildchar != WILDCARD_CHAR && wildchar != WILDCARD_STRING)
						{
							justWildcardsLeft = false;
						}
						else
						{
							// to prevent "cat" matches "ca??"
							if (wildchar == WILDCARD_CHAR)
							{
								return false;
							}
							
							// Look at the next character
							wildcardSearchPos++;
						}
					}
					
					// This was a prefix wildcard search, and we've matched, so
					// return true.
					if (justWildcardsLeft)
					{
						return true;
					}
				}
				
				// If we've gone past the end of the string, or the pattern,
				// return false.
				if (sEnd || pEnd)
				{
					break;
				}
				
				// Match a single character, so continue.
				if (pattern[p] == WILDCARD_CHAR)
				{
					continue;
				}
				
				//
				if (pattern[p] == WILDCARD_STRING)
				{
					// Look at the character beyond the '*'.
					++p;
					// Examine the string, starting at the last character.
					for (int i = string_Renamed.Length; i >= s; --i)
					{
						if (WildcardEquals(pattern, p, string_Renamed, i))
						{
							return true;
						}
					}
					break;
				}
				if (pattern[p] != string_Renamed[s])
				{
					break;
				}
			}
			return false;
		}
		
		public override void  Close()
		{
			base.Close();
			searchTerm = null;
			field = null;
			text = null;
		}
	}
}