#include "KinoSearch/Util/ToolSet.h"

#define KINO_WANT_POSTINGPOOLQUEUE_VTABLE
#include "KinoSearch/Index/PostingPoolQueue.r"

#include "KinoSearch/Index/PostingPool.r"
#include "KinoSearch/Posting/RawPosting.r"
#include "KinoSearch/Store/InStream.r"
#include "KinoSearch/Util/IntMap.r"

#define DOESNT_MATTER 1024

PostingPoolQueue*
PostPoolQ_new(VArray *post_pools, InStream *lex_instream,
              InStream *post_instream, IntMap *pre_sort_map, u32_t mem_thresh)
{
    MSort_compare_t compare = pre_sort_map == NULL 
        ? PostPoolQ_compare_rawp
        : PostPoolQ_compare_rawp_for_pre_sort;
    u32_t i;
    u32_t sub_thresh = post_pools->size > 0 
        ? mem_thresh / post_pools->size 
        : mem_thresh;
    CREATE(self, PostingPoolQueue, POSTINGPOOLQUEUE);

    /* init */
    kino_SortEx_init_base((SortExternal*)self, DOESNT_MATTER, compare);

    /* assign */
    for (i = 0; i < post_pools->size; i++) {
        PostingPool *post_pool = (PostingPool*)VA_Fetch(post_pools, i);
        if (post_pool != NULL) {
            PostPoolQ_Add_Run(self, (SortExRun*)post_pool);
            PostPool_Flip(post_pool, lex_instream, post_instream, sub_thresh);
        }
    }
    if (pre_sort_map != NULL)
        self->context = (Obj*)REFCOUNT_INC(pre_sort_map);

    /* always in read mode */
    self->flipped = true;

    return self;
}

void
PostPoolQ_flip(PostingPoolQueue *self)
{
    self->flipped = true;
}

int
PostPoolQ_compare_rawp(void *context, const void *va, const void *vb)
{
    RawPosting *const a = *(RawPosting**)va;
    RawPosting *const b = *(RawPosting**)vb;
    const size_t a_len = a->content_len;
    const size_t b_len = b->content_len;
    const size_t len = a_len < b_len? a_len : b_len;
    int comparison = memcmp(a->blob, b->blob, len);

    UNUSED_VAR(context);

    if (comparison == 0) {
        /* if a is a substring of b, it's less than b, so return a neg num */
        if (len > 0)
            comparison = a_len - b_len;

        /* break ties by doc num */
        if (comparison == 0) 
            comparison = a->doc_num - b->doc_num;
    }

    return comparison;
}

int
PostPoolQ_compare_rawp_for_pre_sort(void *context, 
                                    const void *va, const void *vb)
{
    RawPosting *const a = *(RawPosting**)va;
    RawPosting *const b = *(RawPosting**)vb;
    const size_t a_len = a->content_len;
    const size_t b_len = b->content_len;
    const size_t len = a_len < b_len? a_len : b_len;
    int comparison = memcmp(a->blob, b->blob, len);

    UNUSED_VAR(context);

    if (comparison == 0) {
        /* if a is a substring of b, it's less than b, so return a neg num */
        if (len > 0)
            comparison = a_len - b_len;

        /* break ties by doc num */
        if (comparison == 0) {  /* only difference is here */
            IntMap *const doc_remap = (IntMap*)context;
            const u32_t doc_num_a = IntMap_Get(doc_remap, a->doc_num);
            const u32_t doc_num_b = IntMap_Get(doc_remap, b->doc_num);
            comparison = doc_num_a - doc_num_b;
        }
    }

    return comparison;
}

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