using Lucene.Net.Index;
using System.Collections.Generic;
using System.Linq;

namespace Lucene.Net.Search.Grouping
{
    /*
	 * 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.
	 */

    /// <summary>
    /// A collector that collects all groups that match the
    /// query. Only the group value is collected, and the order
    /// is undefined.  This collector does not determine
    /// the most relevant document of a group.
    /// 
    /// <para>
    /// This is an abstract version. Concrete implementations define
    /// what a group actually is and how it is internally collected.
    /// </para>
    /// @lucene.experimental
    /// </summary>
    /// <typeparam name="TGroupValue"></typeparam>
    public abstract class AbstractAllGroupsCollector<TGroupValue> : IAbstractAllGroupsCollector<TGroupValue>
    {
        /// <summary>
        /// Returns the total number of groups for the executed search.
        /// This is a convenience method. The following code snippet has the same effect: <code>GetGroups().Count</code>
        /// </summary>
        /// <returns>The total number of groups for the executed search</returns>
        public virtual int GroupCount
        {
            get
            {
                return Groups.Count();
            }
        }

        /// <summary>
        /// Returns the group values
        /// <para>
        /// This is an unordered collections of group values. For each group that matched the query there is a <see cref="Util.BytesRef"/>
        /// representing a group value.
        /// </para>
        /// </summary>
        /// <returns>the group values</returns>
        public abstract IEnumerable<TGroupValue> Groups { get; }


        // Empty not necessary
        public virtual void SetScorer(Scorer scorer)
        {
        }

        // LUCENENET specific - we need to implement these here, since our abstract base class
        // is now an interface.

        /// <summary>
        /// Called once for every document matching a query, with the unbased document
        /// number.
        /// <para/>Note: The collection of the current segment can be terminated by throwing
        /// a <see cref="CollectionTerminatedException"/>. In this case, the last docs of the
        /// current <see cref="AtomicReaderContext"/> will be skipped and <see cref="IndexSearcher"/>
        /// will swallow the exception and continue collection with the next leaf.
        /// <para/>
        /// Note: this is called in an inner search loop. For good search performance,
        /// implementations of this method should not call <see cref="IndexSearcher.Doc(int)"/> or
        /// <see cref="Lucene.Net.Index.IndexReader.Document(int)"/> on every hit.
        /// Doing so can slow searches by an order of magnitude or more.
        /// </summary>
        public abstract void Collect(int doc);

        /// <summary>
        /// Called before collecting from each <see cref="AtomicReaderContext"/>. All doc ids in
        /// <see cref="Collect(int)"/> will correspond to <see cref="Index.IndexReaderContext.Reader"/>.
        ///
        /// Add <see cref="AtomicReaderContext.DocBase"/> to the current <see cref="Index.IndexReaderContext.Reader"/>'s
        /// internal document id to re-base ids in <see cref="Collect(int)"/>.
        /// </summary>
        /// <param name="context">next atomic reader context </param>
        public abstract void SetNextReader(AtomicReaderContext context);

        public virtual bool AcceptsDocsOutOfOrder
        {
            get { return true; }
        }
    }

    /// <summary>
    /// LUCENENET specific interface used to apply covariance to TGroupValue
    /// </summary>
    /// <typeparam name="TGroupValue"></typeparam>
    public interface IAbstractAllGroupsCollector<out TGroupValue> : ICollector
    {
        /// <summary>
        /// Returns the total number of groups for the executed search.
        /// This is a convenience method. The following code snippet has the same effect: <code>GetGroups().Count</code>
        /// </summary>
        /// <returns>The total number of groups for the executed search</returns>
        int GroupCount { get; }

        /// <summary>
        /// Returns the group values
        /// <para>
        /// This is an unordered collections of group values. For each group that matched the query there is a <see cref="Util.BytesRef"/>
        /// representing a group value.
        /// </para>
        /// </summary>
        /// <returns>the group values</returns>
        IEnumerable<TGroupValue> Groups { get; }
    }
}
