/** @file
 * @brief PostList class implementing Query::OP_AND_NOT
 */
/* Copyright 2017,2022 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 "andnotpostlist.h"

#include <algorithm>

using namespace std;

PostList*
AndNotPostList::next(double w_min)
{
    while (true) {
        PostList* result = pl->next(w_min);
        if (result) {
            delete pl;
            pl = result;
        }
        if (pl->at_end()) {
            result = pl;
            pl = NULL;
            return result;
        }
        Xapian::docid l_did = pl->get_docid();
        if (l_did > r_did) {
            bool r_valid;
            result = r->check(l_did, 0, r_valid);
            if (result) {
                delete r;
                r = result;
            }
            if (!r_valid)
                return NULL;
            if (r->at_end()) {
                result = pl;
                pl = NULL;
                return result;
            }
            r_did = r->get_docid();
        }
        if (l_did < r_did)
            break;
    }
    return NULL;
}

PostList*
AndNotPostList::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 (pl->at_end()) {
            result = pl;
            pl = NULL;
            return result;
        }
        Xapian::docid l_did = pl->get_docid();
        if (l_did > r_did) {
            bool r_valid;
            result = r->check(l_did, 0, r_valid);
            if (result) {
                delete r;
                r = result;
            }
            if (!r_valid)
                return NULL;
            if (r->at_end()) {
                result = pl;
                pl = NULL;
                return result;
            }
            r_did = r->get_docid();
        }
        if (l_did == r_did) {
            // Advance to the next match.
            return AndNotPostList::next(w_min);
        }
    }
    return NULL;
}

PostList*
AndNotPostList::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) {
        if (pl->at_end()) {
            result = pl;
            pl = NULL;
            return result;
        }
        Xapian::docid l_did = pl->get_docid();
        if (l_did > r_did) {
            bool r_valid;
            result = r->check(l_did, 0, r_valid);
            if (result) {
                delete r;
                r = result;
            }
            if (!r_valid)
                return NULL;
            if (r->at_end()) {
                result = pl;
                pl = NULL;
                return result;
            }
            r_did = r->get_docid();
        }
        if (l_did == r_did) {
            // For check() we can simply indicate !valid.
            valid = false;
        }
    }
    return NULL;
}

string
AndNotPostList::get_description() const
{
    string desc = "AndNotPostList(";
    desc += pl->get_description();
    desc += ", ";
    desc += r->get_description();
    desc += ')';
    return desc;
}
