/*
 *  call-seq:
 *     searcher.scan(query, options = {}) -> Array (doc_nums)
 *
 *  Run a query through the Searcher on the index, ignoring scoring and
 *  starting at +:start_doc+ and stopping when +:limit+ matches have been
 *  found. It returns an array of the matching document numbers.
 *
 *  There is a big performance advange when using this search method on a very
 *  large index when there are potentially thousands of matching documents and
 *  you only want say 50 of them. The other search methods need to look at
 *  every single match to decide which one has the highest score. This search
 *  method just needs to find +:limit+ number of matches before it returns.
 *  
 *  === Options
 *
 *  :start_doc::    Default: 0. The start document to start the search from.
 *                  NOTE very carefully that this is not the same as the
 *                  +:offset+ parameter used in the other search methods which
 *                  refers to the offset in the result-set. This is the
 *                  document to start the scan from. So if you scanning
 *                  through the index in increments of 50 documents at a time
 *                  you need to use the last matched doc in the previous
 *                  search to start your next search. See the example below.
 *  :limit::        Default: 50. This is the number of results you want
 *                  returned, also called the page size. Set +:limit+ to
 *                  +:all+ to return all results.
 *  TODO: add option to return loaded documents instead
 *
 *  === Options
 *
 *    start_doc = 0
 *    begin
 *      results = @searcher.scan(query, :start_doc => start_doc)
 *      yield results # or do something with them
 *      start_doc = results.last
 *      # start_doc will be nil now if results is empty, ie no more matches
 *    end while start_doc
 */
static VALUE
frb_sea_scan(int argc, VALUE *argv, VALUE self)
{
    Query *q;
    int i, count;
    VALUE rval, rquery, roptions;
    int *doc_array;
    VALUE rdoc_array;
    int start_doc = 0, limit = 50;
    GET_SEA();
    rb_scan_args(argc, argv, "11", &rquery, &roptions);
    Data_Get_Struct(rquery, Query, q);

    if (Qnil != roptions) {
        Check_Type(roptions, T_HASH);
        if (Qnil != (rval = rb_hash_aref(roptions, sym_start_doc))) {
            Check_Type(rval, T_FIXNUM);
            start_doc = FIX2INT(rval);
            if (start_doc < 0) {
                rb_raise(rb_eArgError, ":start_doc must be >= 0");
            }
        }
        if (Qnil != (rval = rb_hash_aref(roptions, sym_limit))) {
            if (TYPE(rval) == T_FIXNUM) {
                limit = FIX2INT(rval);
                if (limit <= 0) {
                    rb_raise(rb_eArgError, ":limit must be > 0");
                }
            }
            else if (rval == sym_all) {
                limit = INT_MAX;
            }
            else {
                rb_raise(rb_eArgError, "%s is not a sensible :limit value "
                         "Please use a positive integer or :all",
                         rs2s(rb_obj_as_string(rval)));
            }
        }
    }

#ifndef FRT_RUBY_VERSION_1_9
    rb_thread_critical = Qtrue;
#endif
    doc_array = ALLOC_N(int, limit);
    count = searcher_search_unscored(sea, q, doc_array, limit, start_doc);
    rdoc_array = rb_ary_new2(count);
    for (i = 0; i < count; i++) {
      rb_ary_store(rdoc_array, i, INT2FIX(doc_array[i]));
    }
    free(doc_array);
#ifndef FRT_RUBY_VERSION_1_9
    rb_thread_critical = 0;
#endif
    return rdoc_array;
}