using System;

namespace Lucene.Net.Search
{
    /*
     * 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 Scorer for OR like queries, counterpart of <code>ConjunctionScorer</code>.
    /// this Scorer implements <seealso cref="Scorer#advance(int)"/> and uses advance() on the given Scorers.
    /// </summary>
#if FEATURE_SERIALIZABLE
    [Serializable]
#endif
    internal class DisjunctionSumScorer : DisjunctionScorer
    {
        /// <summary>
        /// The number of subscorers that provide the current match. </summary>
        protected internal int m_nrMatchers = -1;

        protected internal double m_score = float.NaN;
        private readonly float[] coord;

        /// <summary>
        /// Construct a <code>DisjunctionScorer</code>. </summary>
        /// <param name="weight"> The weight to be used. </param>
        /// <param name="subScorers"> Array of at least two subscorers. </param>
        /// <param name="coord"> Table of coordination factors </param>
        internal DisjunctionSumScorer(Weight weight, Scorer[] subScorers, float[] coord)
            : base(weight, subScorers)
        {
            if (m_numScorers <= 1)
            {
                throw new System.ArgumentException("There must be at least 2 subScorers");
            }
            this.coord = coord;
        }

        protected override void AfterNext()
        {
            Scorer sub = m_subScorers[0];
            m_doc = sub.DocID;
            if (m_doc != NO_MORE_DOCS)
            {
                m_score = sub.GetScore();
                m_nrMatchers = 1;
                CountMatches(1);
                CountMatches(2);
            }
        }

        // TODO: this currently scores, but so did the previous impl
        // TODO: remove recursion.
        // TODO: if we separate scoring, out of here,
        // then change freq() to just always compute it from scratch
        private void CountMatches(int root)
        {
            if (root < m_numScorers && m_subScorers[root].DocID == m_doc)
            {
                m_nrMatchers++;
                m_score += m_subScorers[root].GetScore();
                CountMatches((root << 1) + 1);
                CountMatches((root << 1) + 2);
            }
        }

        /// <summary>
        /// Returns the score of the current document matching the query.
        /// Initially invalid, until <seealso cref="#nextDoc()"/> is called the first time.
        /// </summary>
        public override float GetScore()
        {
            return (float)m_score * coord[m_nrMatchers];
        }

        public override int Freq
        {
            get { return m_nrMatchers; }
        }
    }
}