package KinoSearch::Search::HitQueue;
use strict;
use warnings;
use KinoSearch::Util::ToolSet;
use base qw( KinoSearch::Util::PriorityQueue );

BEGIN { __PACKAGE__->init_instance_vars() }

use KinoSearch::Search::Hit;

sub new {
    my $either = shift;
    my $self   = $either->SUPER::new(@_);

    $self->define_less_than;

    return $self;
}

# Create an array of "empty" Hit objects -- they have scores and ids,
# but the stored fields have yet to be retrieved.
sub hits {
    my ( $self, $start_offset, $num_wanted, $searcher ) = @_;
    my @hits = @{ $self->pop_all };

    if ( defined $start_offset and defined $num_wanted ) {
        @hits = splice( @hits, $start_offset, $num_wanted );
    }

    @hits = map {
        KinoSearch::Search::Hit->new(
            id       => unpack( 'N', "$_" ),
            score    => 0 + $_,
            searcher => $searcher
            )
    } @hits;

    return \@hits;
}

1;

__END__
__XS__

MODULE = KinoSearch    PACKAGE = KinoSearch::Search::HitQueue

void
define_less_than(hitq)
    PriorityQueue *hitq;
PPCODE:
    hitq->less_than = &Kino_HitQ_less_than;

__H__

#ifndef H_KINOSEARCH_SEARCH_HIT_QUEUE
#define H_KINOSEARCH_SEARCH_HIT_QUEUE 1

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

bool Kino_HitQ_less_than(SV*, SV*);

#endif /* include guard */

__C__

#include "KinoSearchSearchHitQueue.h"

/* Compare the NV then the PV for two scalars. 
 */
bool
Kino_HitQ_less_than(SV* a, SV* b) {
    char *ptr_a, *ptr_b; 

    if (SvNV(a) == SvNV(b)) {
        ptr_a = SvPVX(a);
        ptr_b = SvPVX(b);
        /* sort by doc_num second */
        return (bool) (memcmp(ptr_b, ptr_a, 4) < 0);
    }
    /* sort by score first */
    return SvNV(a) < SvNV(b);
}


__POD__

=begin devdocs

=head1 NAME

KinoSearch::Search::HitQueue - track highest scoring docs

=head1 DESCRIPTION 

HitQueue, a subclass of KinoSearch::Util::PriorityQueue, keeps track of
score/doc_num pairs.  Each pair is stored in a single scalar, with the
document number in the PV and the score in the NV.
The encoding algorithm is functionally equivalent to this:

    my $encoded_doc_num = pack('N', $doc_num);
    my $doc_num_slash_score = dualvar( $score, $encoded_doc_num );

=head1 COPYRIGHT

Copyright 2005-2006 Marvin Humphrey

=head1 LICENSE, DISCLAIMER, BUGS, etc.

See L<KinoSearch|KinoSearch> version 0.15.

=end devdocs
=cut