/** @file
 * @brief Base class for classes which filter another PostList
 */
/* Copyright 2017-2026 Olly Betts
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see
 * <https://www.gnu.org/licenses/>.
 */

#include <config.h>

#include "selectpostlist.h"

#include "estimateop.h"
#include "omassert.h"
#include "postlisttree.h"

bool
SelectPostList::vet(double w_min)
{
    if (pl->at_end()) {
        delete pl;
        pl = NULL;
        return true;
    }

    // We assume that test_doc() is expensive compared to calculating the
    // weight.
    if (w_min <= 0.0) {
        cached_weight = -HUGE_VAL;
    } else {
        Xapian::termcount doclen = 0;
        Xapian::termcount unique_terms = 0;
        Xapian::termcount wdfdocmax = 0;
        pltree->get_doc_stats(pl->get_docid(), doclen, unique_terms, wdfdocmax);
        cached_weight = pl->get_weight(doclen, unique_terms, wdfdocmax);
        if (cached_weight < w_min)
            return false;
    }
    return test_doc();
}

SelectPostList::~SelectPostList()
{
    if (estimate_op && (accepted || rejected)) {
        // Only call report_ratio() if there are counts.  During the building
        // of the PostList tree we sometimes need to delete PostList objects
        // and their associated EstimateOp and it's hard to arrange that they
        // are always deleted in the correct order.
        estimate_op->report_ratio(accepted, rejected);
    }
}

double
SelectPostList::get_weight(Xapian::termcount doclen,
                           Xapian::termcount unique_terms,
                           Xapian::termcount wdfdocmax) const
{
    if (cached_weight >= 0)
        return cached_weight;
    return pl->get_weight(doclen, unique_terms, wdfdocmax);
}

bool
SelectPostList::at_end() const
{
    return pl == NULL;
}

PostList*
SelectPostList::next(double w_min)
{
    do {
        PostList* result = pl->next(w_min);
        if (result) {
            delete pl;
            pl = result;
        }
    } while (!vet(w_min));
    return NULL;
}

PostList*
SelectPostList::skip_to(Xapian::docid did, double w_min)
{
    if (did > pl->get_docid()) {
        PostList* result = pl->skip_to(did, w_min);
        if (result) {
            delete pl;
            pl = result;
        }
        if (!vet(w_min)) {
            // Advance to the next match.
            return SelectPostList::next(w_min);
        }
    }
    return NULL;
}

PostList*
SelectPostList::check(Xapian::docid did, double w_min, bool& valid)
{
    PostList* result = pl->check(did, w_min, valid);
    if (result) {
        delete pl;
        pl = result;
    }
    if (valid) {
        // For check() we can simply indicate !valid if the vetting fails.
        valid = vet(w_min);
    }
    return NULL;
}
