#define C_KINO_ORQUERY
#define C_KINO_ORCOMPILER
#include "KinoSearch/Util/ToolSet.h"

#include "KinoSearch/Search/ORQuery.h"
#include "KinoSearch/Index/SegReader.h"
#include "KinoSearch/Index/Similarity.h"
#include "KinoSearch/Search/ORMatcher.h"
#include "KinoSearch/Search/Searcher.h"
#include "KinoSearch/Store/InStream.h"
#include "KinoSearch/Store/OutStream.h"

ORQuery*
ORQuery_new(VArray *children)
{
    ORQuery *self = (ORQuery*)VTable_Make_Obj(ORQUERY);
    return ORQuery_init(self, children);
}

ORQuery*
ORQuery_init(ORQuery *self, VArray *children)
{
    return (ORQuery*)PolyQuery_init((PolyQuery*)self, children);
}

Compiler*
ORQuery_make_compiler(ORQuery *self, Searcher *searcher, float boost)
{
    return (Compiler*)ORCompiler_new(self, searcher, boost);
}

bool_t
ORQuery_equals(ORQuery *self, Obj *other)
{
    if ((ORQuery*)other == self)   { return true;  }
    if (!Obj_Is_A(other, ORQUERY)) { return false; }
    return PolyQuery_equals((PolyQuery*)self, other);
}

CharBuf*
ORQuery_to_string(ORQuery *self)
{
    uint32_t num_kids = VA_Get_Size(self->children);
    if (!num_kids) return CB_new_from_trusted_utf8("()", 2);
    else {
        CharBuf *retval = CB_new_from_trusted_utf8("(", 1);
        uint32_t i;
        uint32_t last_kid = num_kids - 1;
        for (i = 0; i < num_kids; i++) {
            CharBuf *kid_string = Obj_To_String(VA_Fetch(self->children, i));
            CB_Cat(retval, kid_string);
            DECREF(kid_string);
            if (i == last_kid) {
                CB_Cat_Trusted_Str(retval, ")", 1);
            }
            else {
                CB_Cat_Trusted_Str(retval, " OR ", 4);
            }
        }
        return retval;
    }
}

/**********************************************************************/

ORCompiler*
ORCompiler_new(ORQuery *parent, Searcher *searcher, float boost)
{
    ORCompiler *self = (ORCompiler*)VTable_Make_Obj(ORCOMPILER);
    return ORCompiler_init(self, parent, searcher, boost);
}

ORCompiler*
ORCompiler_init(ORCompiler *self, ORQuery *parent, Searcher *searcher, 
                 float boost)
{
    PolyCompiler_init((PolyCompiler*)self, (PolyQuery*)parent, searcher, 
        boost);
    ORCompiler_Normalize(self);
    return self;
}

Matcher*
ORCompiler_make_matcher(ORCompiler *self, SegReader *reader, 
                        bool_t need_score)
{
    uint32_t num_kids = VA_Get_Size(self->children);

    if (num_kids == 1) {
        Compiler *only_child = (Compiler*)VA_Fetch(self->children, 0);
        return Compiler_Make_Matcher(only_child, reader, need_score);
    }
    else {
        VArray *submatchers = VA_new(num_kids);
        uint32_t i;
        uint32_t num_submatchers = 0;

        // Accumulate sub-matchers. 
        for (i = 0; i < num_kids; i++) {
            Compiler *child = (Compiler*)VA_Fetch(self->children, i);
            Matcher *submatcher 
                = Compiler_Make_Matcher(child, reader, need_score);
            if (submatcher != NULL) {
                VA_Push(submatchers, (Obj*)submatcher);
                num_submatchers++;
            }
        }

        if (num_submatchers == 0) {
            // No possible matches, so return null. 
            DECREF(submatchers);
            return NULL;
        }
        else if (num_submatchers == 1) {
            // Only one submatcher, so no need for ORScorer wrapper. 
            Matcher *submatcher = (Matcher*)INCREF(VA_Fetch(submatchers, 0));
            DECREF(submatchers);
            return submatcher;
        }
        else {
            Similarity *sim    = ORCompiler_Get_Similarity(self);
            Matcher    *retval = need_score
                ? (Matcher*)ORScorer_new(submatchers, sim)
                : (Matcher*)ORMatcher_new(submatchers);
            DECREF(submatchers);
            return retval;
        }
    }
}

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

