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

namespace Lucene.Net.Search
{
	
	/// <summary>A Scorer for queries with a required part and an optional part.
	/// Delays skipTo() on the optional part until a score() is needed.
	/// <br>
	/// This <code>Scorer</code> implements {@link Scorer#SkipTo(int)}.
	/// </summary>
	public class ReqOptSumScorer : Scorer
	{
		/// <summary>The scorers passed from the constructor.
		/// These are set to null as soon as their next() or skipTo() returns false.
		/// </summary>
		private Scorer reqScorer;
		private Scorer optScorer;
		
		/// <summary>Construct a <code>ReqOptScorer</code>.</summary>
		/// <param name="reqScorer">The required scorer. This must match.
		/// </param>
		/// <param name="optScorer">The optional scorer. This is used for scoring only.
		/// </param>
		public ReqOptSumScorer(Scorer reqScorer, Scorer optScorer) : base(null)
		{ // No similarity used.
			this.reqScorer = reqScorer;
			this.optScorer = optScorer;
		}
		
		private bool firstTimeOptScorer = true;
		
		public override bool Next()
		{
			return reqScorer.Next();
		}
		
		public override bool SkipTo(int target)
		{
			return reqScorer.SkipTo(target);
		}
		
		public override int Doc()
		{
			return reqScorer.Doc();
		}
		
		/// <summary>Returns the score of the current document matching the query.
		/// Initially invalid, until {@link #Next()} is called the first time.
		/// </summary>
		/// <returns> The score of the required scorer, eventually increased by the score
		/// of the optional scorer when it also matches the current document.
		/// </returns>
		public override float Score()
		{
			int curDoc = reqScorer.Doc();
			float reqScore = reqScorer.Score();
			if (firstTimeOptScorer)
			{
				firstTimeOptScorer = false;
				if (!optScorer.SkipTo(curDoc))
				{
					optScorer = null;
					return reqScore;
				}
			}
			else if (optScorer == null)
			{
				return reqScore;
			}
			else if ((optScorer.Doc() < curDoc) && (!optScorer.SkipTo(curDoc)))
			{
				optScorer = null;
				return reqScore;
			}
			// assert (optScorer != null) && (optScorer.doc() >= curDoc);
			return (optScorer.Doc() == curDoc)?reqScore + optScorer.Score():reqScore;
		}
		
		/// <summary>Explain the score of a document.</summary>
		/// <todo>  Also show the total score. </todo>
		/// <summary> See BooleanScorer.explain() on how to do this.
		/// </summary>
		public override Explanation Explain(int doc)
		{
			Explanation res = new Explanation();
			res.SetDescription("required, optional");
			res.AddDetail(reqScorer.Explain(doc));
			res.AddDetail(optScorer.Explain(doc));
			return res;
		}
	}
}