/* 
 * 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;
using StringHelper = Lucene.Net.Util.StringHelper;

namespace Lucene.Net.Search
{
	
	/// <summary> Subclass of FilteredTermEnum for enumerating all terms that match the
	/// specified range parameters.
	/// <p/>
	/// Term enumerations are always ordered by Term.compareTo().  Each term in
	/// the enumeration is greater than all that precede it.
	/// </summary>
	/// <since> 2.9
	/// </since>
	public class TermRangeTermEnum:FilteredTermEnum
	{
		
		private System.Globalization.CompareInfo collator = null;
		private bool endEnum = false;
		private System.String field;
		private System.String upperTermText;
		private System.String lowerTermText;
		private bool includeLower;
		private bool includeUpper;
		
		/// <summary> Enumerates all terms greater/equal than <c>lowerTerm</c>
		/// but less/equal than <c>upperTerm</c>. 
		/// 
		/// If an endpoint is null, it is said to be "open". Either or both 
		/// endpoints may be open.  Open endpoints may not be exclusive 
		/// (you can't select all but the first or last term without 
		/// explicitly specifying the term to exclude.)
		/// 
		/// </summary>
		/// <param name="reader">
		/// </param>
		/// <param name="field">An interned field that holds both lower and upper terms.
		/// </param>
		/// <param name="lowerTermText">The term text at the lower end of the range
		/// </param>
		/// <param name="upperTermText">The term text at the upper end of the range
		/// </param>
		/// <param name="includeLower">If true, the <c>lowerTerm</c> is included in the range.
		/// </param>
		/// <param name="includeUpper">If true, the <c>upperTerm</c> is included in the range.
		/// </param>
		/// <param name="collator">The collator to use to collate index Terms, to determine their
		/// membership in the range bounded by <c>lowerTerm</c> and
		/// <c>upperTerm</c>.
		/// 
		/// </param>
		/// <throws>  IOException </throws>
		public TermRangeTermEnum(IndexReader reader, System.String field, System.String lowerTermText, System.String upperTermText, bool includeLower, bool includeUpper, System.Globalization.CompareInfo collator)
		{
			this.collator = collator;
			this.upperTermText = upperTermText;
			this.lowerTermText = lowerTermText;
			this.includeLower = includeLower;
			this.includeUpper = includeUpper;
			this.field = StringHelper.Intern(field);
			
			// do a little bit of normalization...
			// open ended range queries should always be inclusive.
			if (this.lowerTermText == null)
			{
				this.lowerTermText = "";
				this.includeLower = true;
			}
			
			if (this.upperTermText == null)
			{
				this.includeUpper = true;
			}
			
			System.String startTermText = collator == null?this.lowerTermText:"";
			SetEnum(reader.Terms(new Term(this.field, startTermText)));
		}
		
		public override float Difference()
		{
			return 1.0f;
		}
		
		public override bool EndEnum()
		{
			return endEnum;
		}

	    protected internal override bool TermCompare(Term term)
		{
			if (collator == null)
			{
				// Use Unicode code point ordering
			    bool checkLower = !includeLower;
			    if (term != null && (System.Object) term.Field == (System.Object) field)
				{
					// interned comparison
					if (!checkLower || null == lowerTermText || String.CompareOrdinal(term.Text, lowerTermText) > 0)
					{
						checkLower = false;
						if (upperTermText != null)
						{
							int compare = String.CompareOrdinal(upperTermText, term.Text);
							/*
							* if beyond the upper term, or is exclusive and this is equal to
							* the upper term, break out
							*/
							if ((compare < 0) || (!includeUpper && compare == 0))
							{
								endEnum = true;
								return false;
							}
						}
						return true;
					}
				}
				else
				{
					// break
					endEnum = true;
					return false;
				}
				return false;
			}
			else
			{
				if (term != null && (System.Object) term.Field == (System.Object) field)
				{
					// interned comparison
					if ((lowerTermText == null || (includeLower?collator.Compare(term.Text.ToString(), lowerTermText.ToString()) >= 0:collator.Compare(term.Text.ToString(), lowerTermText.ToString()) > 0)) && (upperTermText == null || (includeUpper?collator.Compare(term.Text.ToString(), upperTermText.ToString()) <= 0:collator.Compare(term.Text.ToString(), upperTermText.ToString()) < 0)))
					{
						return true;
					}
					return false;
				}
				endEnum = true;
				return false;
			}
		}
	}
}