/* 
 * 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 Explanation = Lucene.Net.Search.Explanation;

namespace Lucene.Net.Search.Function
{
	
	/// <summary> Expert: represents field values as different types.
	/// Normally created via a 
	/// <see cref="Lucene.Net.Search.Function.ValueSource">ValueSuorce</see> 
	/// for a particular field and reader.
	/// 
	/// <p/><font color="#FF0000">
	/// WARNING: The status of the <b>Search.Function</b> package is experimental. 
	/// The APIs introduced here might change in the future and will not be 
	/// supported anymore in such a case.</font>
	/// 
	/// 
	/// </summary>
	public abstract class DocValues
	{
		/*
		* DocValues is distinct from ValueSource because
		* there needs to be an object created at query evaluation time that
		* is not referenced by the query itself because:
		* - Query objects should be MT safe
		* - For caching, Query objects are often used as keys... you don't
		*   want the Query carrying around big objects
		*/
		
		/// <summary> Return doc value as a float. 
		/// <p/>Mandatory: every DocValues implementation must implement at least this method. 
		/// </summary>
		/// <param name="doc">document whose float value is requested. 
		/// </param>
		public abstract float FloatVal(int doc);
		
		/// <summary> Return doc value as an int. 
		/// <p/>Optional: DocValues implementation can (but don't have to) override this method. 
		/// </summary>
		/// <param name="doc">document whose int value is requested.
		/// </param>
		public virtual int IntVal(int doc)
		{
			return (int) FloatVal(doc);
		}
		
		/// <summary> Return doc value as a long. 
		/// <p/>Optional: DocValues implementation can (but don't have to) override this method. 
		/// </summary>
		/// <param name="doc">document whose long value is requested.
		/// </param>
		public virtual long LongVal(int doc)
		{
			return (long) FloatVal(doc);
		}
		
		/// <summary> Return doc value as a double. 
		/// <p/>Optional: DocValues implementation can (but don't have to) override this method. 
		/// </summary>
		/// <param name="doc">document whose double value is requested.
		/// </param>
		public virtual double DoubleVal(int doc)
		{
			return (double) FloatVal(doc);
		}
		
		/// <summary> Return doc value as a string. 
		/// <p/>Optional: DocValues implementation can (but don't have to) override this method. 
		/// </summary>
		/// <param name="doc">document whose string value is requested.
		/// </param>
		public virtual System.String StrVal(int doc)
		{
			return FloatVal(doc).ToString();
		}
		
		/// <summary> Return a string representation of a doc value, as reuired for Explanations.</summary>
		public abstract System.String ToString(int doc);
		
		/// <summary> Explain the scoring value for the input doc.</summary>
		public virtual Explanation Explain(int doc)
		{
			return new Explanation(FloatVal(doc), ToString(doc));
		}

	    /// <summary> Expert: for test purposes only, return the inner array of values, or null if not applicable.
	    /// <p/>
	    /// Allows tests to verify that loaded values are:
	    /// <list type="bullet">
	    /// <item>indeed cached/reused.</item>
	    /// <item>stored in the expected size/type (byte/short/int/float).</item>
	    /// </list>
	    /// Note: implementations of DocValues must override this method for 
	    /// these test elements to be tested, Otherwise the test would not fail, just 
	    /// print a warning.
	    /// </summary>
	    protected internal virtual object InnerArray
	    {
	        get { throw new System.NotSupportedException("this optional method is for test purposes only"); }
	    }

	    // --- some simple statistics on values
		private float minVal = System.Single.NaN;
		private float maxVal = System.Single.NaN;
		private float avgVal = System.Single.NaN;
		private bool computed = false;
		// compute optional values
		private void  Compute()
		{
			if (computed)
			{
				return ;
			}
			float sum = 0;
			int n = 0;
			while (true)
			{
				float val;
				try
				{
					val = FloatVal(n);
				}
				catch (System.IndexOutOfRangeException)
				{
					break;
				}
				sum += val;
				minVal = System.Single.IsNaN(minVal)?val:System.Math.Min(minVal, val);
				maxVal = System.Single.IsNaN(maxVal)?val:System.Math.Max(maxVal, val);
				++n;
			}
			
			avgVal = n == 0?System.Single.NaN:sum / n;
			computed = true;
		}
		
		/// <summary> Returns the minimum of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value.
		/// <p/>
		/// This operation is optional
		/// <p/>
		/// 
		/// </summary>
		/// <returns> the minimum of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value.
		/// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
        public virtual float GetMinValue()
		{
			Compute();
			return minVal;
		}
		
		/// <summary> Returns the maximum of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value.
		/// <p/>
		/// This operation is optional
		/// <p/>
		/// 
		/// </summary>
		/// <returns> the maximum of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
		public virtual float GetMaxValue()
		{
			Compute();
			return maxVal;
		}
		
		/// <summary> Returns the average of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value. *
		/// <p/>
		/// This operation is optional
		/// <p/>
		/// 
		/// </summary>
		/// <returns> the average of all values or <c>Float.NaN</c> if this
		/// DocValues instance does not contain any value
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
		public virtual float GetAverageValue()
		{
			Compute();
			return avgVal;
		}
	}
}