#include "KinoSearch/Util/ToolSet.h"

#define KINO_WANT_SCORER_VTABLE
#include "KinoSearch/Search/Scorer.r"

#include "KinoSearch/Search/HitCollector.r"
#include "KinoSearch/Search/Tally.r"
#include "KinoSearch/Util/Int.r"


Tally*
Scorer_tally(Scorer *self) 
{
    ABSTRACT_DEATH(self, "Tally");
    UNREACHABLE_RETURN(Tally*);
}

bool_t
Scorer_next(Scorer *self) 
{
    ABSTRACT_DEATH(self, "Next");
    UNREACHABLE_RETURN(bool_t);
}

u32_t
Scorer_doc(Scorer *self) 
{
    ABSTRACT_DEATH(self, "Doc");
    UNREACHABLE_RETURN(u32_t);
}

bool_t
Scorer_skip_to(Scorer *self, u32_t target) 
{
    do {
        if ( !Scorer_Next(self) )
            return false;
    } while ( target > Scorer_Doc(self) );

    return true;
}


void
Scorer_collect(Scorer *self, HitCollector *hc, u32_t start, u32_t end, 
               u32_t hits_per_seg, VArray *seg_starts)
{
    u32_t              seg_num          = 0;
    u32_t              doc_num_thresh   = 0;
    u32_t              hits_this_seg    = 0;
    u32_t              hits_thresh      = hits_per_seg;

    /* get to first doc */
    if ( !Scorer_Skip_To(self, start) )
        return;

    /* execute scoring loop */
    do {
        u32_t doc_num = Scorer_Doc(self);
        Tally *tally;

        if (hits_this_seg >= hits_thresh || doc_num >= doc_num_thresh) {
            if (doc_num >= end) {
                /* bail out of loop if we've hit the user-spec'd end */
                return;
            }
            else if (seg_starts == NULL || seg_starts->size == 0) {
                /* must supply seg_starts to enable pruning */
                hits_thresh    = U32_MAX;
                doc_num_thresh = end;
            }
            else if (seg_num == seg_starts->size) {
                /* we've finished the last segment */
                return;
            }
            else {
                /* get start of upcoming segment */
                Int *this_start = (Int*)VA_Fetch(seg_starts, seg_num);
                Int *next_start = (Int*)VA_Fetch(seg_starts, seg_num + 1);
                u32_t this_seg_start = this_start->value;
                seg_num++;

                /* skip docs as appropriate if we're pruning */
                if (doc_num < this_seg_start) {
                    if ( Scorer_Skip_To(self, this_seg_start) )
                        doc_num = Scorer_Doc(self);
                    else
                        return;
                }

                /* set the last doc_num we'll score this upcoming segment */
                doc_num_thresh = next_start == NULL
                    ? end  /* last segment */
                    : next_start->value;
            }

            /* start over counting hits for the new segment */
            hits_this_seg = 0;
        }

        /* this doc is in range, so collect it */
        tally = Scorer_Tally(self);
        hc->collect(hc, doc_num, tally->score);
        hits_this_seg++;
    } while (Scorer_Next(self));
}

u32_t 
Scorer_max_matchers(Scorer *self)
{
    UNUSED_VAR(self);
    return 1;
}

/* Copyright 2006-2007 Marvin Humphrey
 *
 * This program is free software; you can redistribute it and/or modify
 * under the same terms as Perl itself.
 */