/*
 * call-seq:
 *  select(string, option=>value, ...)
 *  select(string, recs, option=>value, ...)
 *  select(string, recs, op, option=>value, ...)
 *
 * インデックスの検索。
 *
 * 有効な option:
 * :mode [Integer] :: 次のいずれか。Senna::SEL_EXACT, Senna::SEL_PARTIAL, Senna::SEL_UNSPLIT, Senna::SEL_NEAR, Senna::SEL_SIMILAR, Senna::SEL_PREFIX, Senna::SEL_SUFFIX
 * :similarity_threshold [Integer] :: Senna API DOC 参照
 * :max_interval [Integer] :: Senna API DOC 参照
 * :weight_vector [Array of Integer] :: Senna API DOC 参照
 * :vector_size [Integer] :: Senna API DOC 参照
 *
 * === Argument
 * string [String] :: 検索する文字列
 * recs [Senna::Records] :: 結果が反映されるレコード群。未指定時は空の Senna::Records が作成される。
 * op [Integer] :: 次のいずれか。Senna::SEL_OR(default), Senna::SEL_AND, Senna::SEL_BUT, Senna::SEL_ADJUST
 *
 * === Return
 * Senna::Records :: recs と同じ。
 */
static VALUE index_select(int argc, VALUE *argv, VALUE obj)
{
    VALUE string;
    VALUE arg1, arg2, arg3;
    VALUE recs = Qnil;
    VALUE op = Qnil;
    VALUE optarg = Qnil;
    struct index_data *data;
    sen_records *_recs;
    sen_sel_operator _op;
    sen_select_optarg _optarg, *_optarg_p;
    sen_rc rc;
    int n;
    n = rb_scan_args(argc, argv, "13", &string, &arg1, &arg2, &arg3);
    Check_Type(string, T_STRING);
    switch (n) {
    case 1:
        break;
    case 2:
        if (TYPE(arg1) == T_HASH)
            optarg = arg1;
        else
            recs = arg1;
        break;
    case 3:
        recs = arg1;
        if (TYPE(arg2) == T_HASH)
            optarg = arg2;
        else
            op = arg2;
        break;
    case 4:
        recs = arg1;
        op = arg2;
        optarg = arg3;
        break;
    }
    if (NIL_P(recs)) {
        _recs = sen_records_open(sen_rec_document, sen_rec_none, 0);
        recs = Data_Wrap_Struct(cSennaRecords, 0, sen_records_close, _recs);
    } else {
        if (!rb_obj_is_kind_of(recs, cSennaRecords))
            rb_raise(rb_eTypeError, "wrong argument type %s (expected Senna::Records)", rb_obj_classname(recs));
        Data_Get_Struct(recs, sen_records, _recs);
    }
    _op = NIL_P(op) ? sen_sel_or : NUM2INT(op);
    if (NIL_P(optarg)) {
        _optarg_p = NULL;
    } else {
        VALUE mode, st, mi, wv, vs;
        sen_sel_mode _mode;
        int _st, _mi, *_wv, _vs;
        int i;
        Check_Type(optarg, T_HASH);
        mode = rb_hash_aref(optarg, ID2SYM(rb_intern("mode")));
        _mode = NIL_P(mode) ? sen_sel_exact : NUM2INT(mode);
        st = rb_hash_aref(optarg, ID2SYM(rb_intern("similarity_threshold")));
        _st = NIL_P(st) ? 0 : NUM2INT(st);
        mi = rb_hash_aref(optarg, ID2SYM(rb_intern("max_interval")));
        _mi = NIL_P(mi) ? 0 : NUM2INT(mi);
        wv = rb_hash_aref(optarg, ID2SYM(rb_intern("weight_vector")));
        if (!NIL_P(wv)) {
            Check_Type(wv, T_ARRAY);
            _wv = xmalloc(RARRAY_LEN(wv) * sizeof(int));
            for (i = 0; i < RARRAY_LEN(wv); i += 1) {
                _wv[i] = NUM2INT(RARRAY_PTR(wv)[i]);
            }
            _vs = RARRAY_LEN(wv);
        } else {
            _wv = NULL;
            vs = rb_hash_aref(optarg, ID2SYM(rb_intern("vector_size")));
            _vs = NIL_P(vs) ? 0 : NUM2INT(vs);
        }
        _optarg.mode = _mode;
        _optarg.similarity_threshold = _st;
        _optarg.max_interval = _mi;
        _optarg.weight_vector = _wv;
        _optarg.vector_size = _vs;
        _optarg.func = NULL;
        _optarg_p = &_optarg;
    }
    Data_Get_Struct(obj, struct index_data, data);
    if (data->closed)
        rb_raise(rb_eRuntimeError, "already closed");
    rc = sen_index_select(data->indexp, RSTRING_PTR(string), RSTRING_LEN(string), _recs, _op, _optarg_p);
    if (rc != sen_success)
        senna_error(rc, "sen_index_select failed");
    return recs;
}