﻿using BadMishka.DocumentFormat.LuceneIndex.Index;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BadMishka.DocumentFormat.LuceneIndex.Search
{
    public abstract class PhraseScorer : IScorer
    {
        protected byte[] normalizedBytes;
        protected float weight;

        protected PhraseQueue phraseQueue;
        protected PhraseEnumerator firstPhraseEnumerator, lastPhraseEnumerator;



        public PhraseScorer(IDocumentTermPositionEnumerator[] enumerators, byte[] normalizedBytes, float weight)
        {
            this.normalizedBytes = normalizedBytes;
            this.weight = weight;
            int l = enumerators.Length;


            // use PQ to build a sorted list of PhrasePositions
            this.phraseQueue = new PhraseQueue(l);
            for (int i = 0; i < l; i++)
            {
                this.phraseQueue.Put(new PhraseEnumerator(enumerators[i], i));
            }

            this.ConvertQueueToLinkedList();
        }

        public void Score(Action<int, float> collect, int end)
        {
            while (this.lastPhraseEnumerator.DocumentId < end)
            {              // find doc w/ all the terms
                while (this.firstPhraseEnumerator.DocumentId < this.lastPhraseEnumerator.DocumentId)
                {         // scan forward in first
                    do
                    {
                        this.firstPhraseEnumerator.MoveNext();
                    } while (this.firstPhraseEnumerator.DocumentId < this.lastPhraseEnumerator.DocumentId);
                    this.MoveFirstToLast();

                    if (this.lastPhraseEnumerator.DocumentId >= end)
                        return;
                }

                // found doc with all terms
                float freq = this.GetPhraseFrequency();

                if (freq > 0.0)
                {
                    int documentId = this.firstPhraseEnumerator.DocumentId;
                    float score = SimilarityUtil.CalculateTermFrequency(freq) * weight; // compute score
                    score *= SimilarityUtil.Normalize(this.normalizedBytes[documentId]); // normalize
                    collect(documentId, score);
                }

                this.lastPhraseEnumerator.MoveNext();
            }
        }

        abstract protected float GetPhraseFrequency();

        protected void ConvertQueueToLinkedList()
        {
            this.lastPhraseEnumerator = this.firstPhraseEnumerator = null;
            while (this.phraseQueue.Top() != null)
            {
                var enumerator = this.phraseQueue.Pop();
                if (this.lastPhraseEnumerator != null)
                    this.lastPhraseEnumerator.Next = enumerator;
                else
                    this.firstPhraseEnumerator = enumerator;

                this.lastPhraseEnumerator = enumerator;
                enumerator.Next = null;
            }
        }

        protected void MoveFirstToLast()
        {
            this.lastPhraseEnumerator.Next = this.firstPhraseEnumerator;
            this.lastPhraseEnumerator = this.firstPhraseEnumerator;
            this.firstPhraseEnumerator = this.firstPhraseEnumerator.Next;
            this.lastPhraseEnumerator.Next = null;
        }
    }
}
