/*
 * 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 IndexInput = Lucene.Net.Store.IndexInput;

namespace Lucene.Net.Index
{
	
	public sealed class SegmentTermEnum : TermEnum, System.ICloneable
	{
		private IndexInput input;
		internal FieldInfos fieldInfos;
		internal long size;
		internal long position = - 1;
		
		private TermBuffer termBuffer = new TermBuffer();
		private TermBuffer prevBuffer = new TermBuffer();
		private TermBuffer scratch; // used for scanning
		
		private TermInfo termInfo = new TermInfo();
		
		private int format;
		private bool isIndex = false;
		internal long indexPointer = 0;
		internal int indexInterval;
		internal int skipInterval;
		private int formatM1SkipInterval;
		
		internal SegmentTermEnum(IndexInput i, FieldInfos fis, bool isi)
		{
			input = i;
			fieldInfos = fis;
			isIndex = isi;
			
			int firstInt = input.ReadInt();
			if (firstInt >= 0)
			{
				// original-format file, without explicit format version number
				format = 0;
				size = firstInt;
				
				// back-compatible settings
				indexInterval = 128;
				skipInterval = System.Int32.MaxValue; // switch off skipTo optimization
			}
			else
			{
				// we have a format version number
				format = firstInt;
				
				// check that it is a format we can understand
				if (format < TermInfosWriter.FORMAT)
					throw new System.IO.IOException("Unknown format version:" + format);
				
				size = input.ReadLong(); // read the size
				
				if (format == - 1)
				{
					if (!isIndex)
					{
						indexInterval = input.ReadInt();
						formatM1SkipInterval = input.ReadInt();
					}
					// switch off skipTo optimization for file format prior to 1.4rc2 in order to avoid a bug in 
					// skipTo implementation of these versions
					skipInterval = System.Int32.MaxValue;
				}
				else
				{
					indexInterval = input.ReadInt();
					skipInterval = input.ReadInt();
				}
			}
		}
		
		public System.Object Clone()
		{
			SegmentTermEnum clone = null;
			try
			{
				clone = (SegmentTermEnum) base.MemberwiseClone();
			}
			catch (System.Exception)
			{
			}
			
			clone.input = (IndexInput) input.Clone();
			clone.termInfo = new TermInfo(termInfo);
			
			clone.termBuffer = (TermBuffer) termBuffer.Clone();
			clone.prevBuffer = (TermBuffer) prevBuffer.Clone();
			clone.scratch = null;
			
			return clone;
		}
		
		internal void  Seek(long pointer, int p, Term t, TermInfo ti)
		{
			input.Seek(pointer);
			position = p;
			termBuffer.Set(t);
			prevBuffer.Reset();
			termInfo.Set(ti);
		}
		
		/// <summary>Increments the enumeration to the next element.  True if one exists.</summary>
		public override bool Next()
		{
			if (position++ >= size - 1)
			{
				termBuffer.Reset();
				return false;
			}
			
			prevBuffer.Set(termBuffer);
			termBuffer.Read(input, fieldInfos);
			
			termInfo.docFreq = input.ReadVInt(); // read doc freq
			termInfo.freqPointer += input.ReadVLong(); // read freq pointer
			termInfo.proxPointer += input.ReadVLong(); // read prox pointer
			
			if (format == - 1)
			{
				//  just read skipOffset in order to increment  file pointer; 
				// value is never used since skipTo is switched off
				if (!isIndex)
				{
					if (termInfo.docFreq > formatM1SkipInterval)
					{
						termInfo.skipOffset = input.ReadVInt();
					}
				}
			}
			else
			{
				if (termInfo.docFreq >= skipInterval)
					termInfo.skipOffset = input.ReadVInt();
			}
			
			if (isIndex)
				indexPointer += input.ReadVLong(); // read index pointer
			
			return true;
		}
		
		/// <summary>Optimized scan, without allocating new terms. </summary>
		internal void  ScanTo(Term term)
		{
			if (scratch == null)
				scratch = new TermBuffer();
			scratch.Set(term);
			while (scratch.CompareTo(termBuffer) > 0 && Next())
			{
			}
		}
		
		/// <summary>Returns the current Term in the enumeration.
		/// Initially invalid, valid after next() called for the first time.
		/// </summary>
		public override Term Term()
		{
			return termBuffer.ToTerm();
		}
		
		/// <summary>Returns the previous Term enumerated. Initially null.</summary>
		internal Term Prev()
		{
			return prevBuffer.ToTerm();
		}
		
		/// <summary>Returns the current TermInfo in the enumeration.
		/// Initially invalid, valid after next() called for the first time.
		/// </summary>
		internal TermInfo TermInfo()
		{
			return new TermInfo(termInfo);
		}
		
		/// <summary>Sets the argument to the current TermInfo in the enumeration.
		/// Initially invalid, valid after next() called for the first time.
		/// </summary>
		internal void  TermInfo(TermInfo ti)
		{
			ti.Set(termInfo);
		}
		
		/// <summary>Returns the docFreq from the current TermInfo in the enumeration.
		/// Initially invalid, valid after next() called for the first time.
		/// </summary>
		public override int DocFreq()
		{
			return termInfo.docFreq;
		}
		
		/* Returns the freqPointer from the current TermInfo in the enumeration.
		Initially invalid, valid after next() called for the first time.*/
		internal long FreqPointer()
		{
			return termInfo.freqPointer;
		}
		
		/* Returns the proxPointer from the current TermInfo in the enumeration.
		Initially invalid, valid after next() called for the first time.*/
		internal long ProxPointer()
		{
			return termInfo.proxPointer;
		}
		
		/// <summary>Closes the enumeration to further activity, freeing resources. </summary>
		public override void  Close()
		{
			input.Close();
		}
	}
}