#include "KinoSearch/Util/ToolSet.h"

#define KINO_WANT_ANDORSCORER_VTABLE
#include "KinoSearch/Search/ANDORScorer.r"

#include "KinoSearch/Search/Similarity.r"
#include "KinoSearch/Search/Tally.r"

ANDORScorer*
ANDORScorer_new(Similarity *sim, Scorer *and_scorer, Scorer *or_scorer) 
{
    CREATE(self, ANDORScorer, ANDORSCORER);

    /* assign */
    self->sim              = REFCOUNT_INC(sim);
    self->and_scorer       = REFCOUNT_INC(and_scorer);
    self->or_scorer        = REFCOUNT_INC(or_scorer);

    /* init */
    self->or_scorer_first_time = true;
    self->tally            = Tally_new();

    return self;
}

void
ANDORScorer_destroy(ANDORScorer *self) 
{
    REFCOUNT_DEC(self->sim);
    REFCOUNT_DEC(self->tally);
    REFCOUNT_DEC(self->and_scorer);
    REFCOUNT_DEC(self->or_scorer);

    free(self);
}

bool_t
ANDORScorer_next(ANDORScorer *self)
{
    return Scorer_Next(self->and_scorer);
}

bool_t
ANDORScorer_skip_to(ANDORScorer *self, u32_t target)
{
    return Scorer_Skip_To(self->and_scorer, target);
}

u32_t
ANDORScorer_doc(ANDORScorer *self)
{
    return Scorer_Doc(self->and_scorer);
}

Tally*
ANDORScorer_tally(ANDORScorer *self)
{
    u32_t const current_doc = Scorer_Doc(self->and_scorer);

    if (self->or_scorer_first_time) {
        self->or_scorer_first_time = false;
        if ( !Scorer_Skip_To(self->or_scorer, current_doc) ) {
            REFCOUNT_DEC(self->or_scorer);
            self->or_scorer = NULL;
        }
    }

    if (self->or_scorer == NULL) {
        return Scorer_Tally(self->and_scorer);
    }
    else {
        u32_t or_scorer_doc = Scorer_Doc(self->or_scorer);

        if (or_scorer_doc < current_doc) {
            if ( !Scorer_Skip_To(self->or_scorer, current_doc) ) {
                REFCOUNT_DEC(self->or_scorer);
                self->or_scorer = NULL;
                return Scorer_Tally(self->and_scorer);
            }
            else {
                or_scorer_doc = Scorer_Doc(self->or_scorer);
            }
        }

        if (or_scorer_doc == current_doc) {
            Tally *const tally       = self->tally;
            Tally *const and_tally   = Scorer_Tally(self->and_scorer);
            Tally *const or_tally    = Scorer_Tally(self->or_scorer);

            tally->score = and_tally->score + or_tally->score;
            tally->num_matchers 
                = and_tally->num_matchers + or_tally->num_matchers;

            return tally;
        }
        else {
            return Scorer_Tally(self->and_scorer);
        }
    }
}

u32_t
ANDORScorer_max_matchers(ANDORScorer *self)
{
    return Scorer_Max_Matchers(self->or_scorer) 
         + Scorer_Max_Matchers(self->and_scorer);
}

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