#define C_KINO_REQUIREDOPTIONALSCORER
#include "KinoSearch/Util/ToolSet.h"

#include "KinoSearch/Search/RequiredOptionalScorer.h"
#include "KinoSearch/Index/Similarity.h"

RequiredOptionalScorer*
ReqOptScorer_new(Similarity *similarity, Matcher *required_matcher, 
                 Matcher *optional_matcher) 
{
    RequiredOptionalScorer *self 
        = (RequiredOptionalScorer*)VTable_Make_Obj(REQUIREDOPTIONALSCORER);
    return ReqOptScorer_init(self, similarity, required_matcher, 
        optional_matcher);
}

RequiredOptionalScorer*
ReqOptScorer_init(RequiredOptionalScorer *self, Similarity *similarity, 
                  Matcher *required_matcher, Matcher *optional_matcher) 
{
    VArray *children = VA_new(2);
    VA_Push(children, INCREF(required_matcher));
    VA_Push(children, INCREF(optional_matcher));
    PolyMatcher_init((PolyMatcher*)self, children, similarity);

    // Assign. 
    self->req_matcher       = (Matcher*)INCREF(required_matcher);
    self->opt_matcher       = (Matcher*)INCREF(optional_matcher);

    // Init. 
    self->opt_matcher_first_time = true;

    DECREF(children);
    return self;
}

void
ReqOptScorer_destroy(RequiredOptionalScorer *self) 
{
    DECREF(self->req_matcher);
    DECREF(self->opt_matcher);
    SUPER_DESTROY(self, REQUIREDOPTIONALSCORER);
}

int32_t
ReqOptScorer_next(RequiredOptionalScorer *self)
{
    return Matcher_Next(self->req_matcher);
}

int32_t
ReqOptScorer_advance(RequiredOptionalScorer *self, int32_t target)
{
    return Matcher_Advance(self->req_matcher, target);
}

int32_t
ReqOptScorer_get_doc_id(RequiredOptionalScorer *self)
{
    return Matcher_Get_Doc_ID(self->req_matcher);
}

float
ReqOptScorer_score(RequiredOptionalScorer *self)
{
    int32_t const current_doc = Matcher_Get_Doc_ID(self->req_matcher);

    if (self->opt_matcher_first_time) {
        self->opt_matcher_first_time = false;
        if ( !Matcher_Advance(self->opt_matcher, current_doc) ) {
            DECREF(self->opt_matcher);
            self->opt_matcher = NULL;
        }
    }

    if (self->opt_matcher == NULL) {
        return Matcher_Score(self->req_matcher);
    }
    else {
        int32_t opt_matcher_doc = Matcher_Get_Doc_ID(self->opt_matcher);

        if (opt_matcher_doc < current_doc) {
            opt_matcher_doc = Matcher_Advance(self->opt_matcher, current_doc);
            if (!opt_matcher_doc) {
                DECREF(self->opt_matcher);
                self->opt_matcher = NULL;
                return Matcher_Score(self->req_matcher);
            }
        }

        if (opt_matcher_doc == current_doc) {
            float score = Matcher_Score(self->req_matcher)
                        + Matcher_Score(self->opt_matcher);
            score *= self->coord_factors[2];
            return score;
        }
        else {
            return Matcher_Score(self->req_matcher);
        }
    }
}

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

