#include <fnmatch.h>
#include "ruby.h"

typedef struct wildcard {
    char *pattern;
    int flags;
} wildcard_t;

static VALUE cWildCard;
static VALUE eWildCardError;

static void wildcard_free(wildcard_t *wc)
{
    free(wc->pattern);
    free(wc);
}

static VALUE wildcard_s_new(int argc, VALUE *argv, VALUE self)
{
    VALUE obj, pattern, flags;
    int fl;
    wildcard_t *wc;

    if (rb_scan_args(argc, argv, "11", &pattern, &flags) == 2) {
        fl = NUM2INT(flags);
    }
    else {
        fl = 0;
    }
    Check_Type(pattern, T_STRING);
    obj = Data_Make_Struct(self, wildcard_t, NULL, wildcard_free, wc);
    wc->pattern = ALLOC_N(char, RSTRING(pattern)->len + 1);
    strncpy(wc->pattern, RSTRING(pattern)->ptr, RSTRING(pattern)->len);
    wc->pattern[RSTRING(pattern)->len] = '\0';
    wc->flags = fl;
    return obj;
}

static VALUE wildcard_pattern(VALUE self)
{
    wildcard_t *wc;

    Data_Get_Struct(self, wildcard_t, wc);
    return rb_str_new2(wc->pattern);
}

static VALUE wildcard_flags(VALUE self)
{
    wildcard_t *wc;

    Data_Get_Struct(self, wildcard_t, wc);
    return INT2NUM(wc->flags);
}

static VALUE wildcard_match(VALUE self, VALUE str)
{
    wildcard_t *wc;
    int ret;

    Data_Get_Struct(self, wildcard_t, wc);
    ret = fnmatch(wc->pattern, STR2CSTR(str), wc->flags);
    switch (ret) {
    case 0:
        return Qtrue;
    case FNM_NOMATCH:
        return Qfalse;
    default:
        rb_raise(eWildCardError, "error");
    }
}

void Init_wildcard()
{
    cWildCard = rb_define_class("WildCard", rb_cObject);
    rb_define_singleton_method(cWildCard, "new", wildcard_s_new, -1);
    rb_define_method(cWildCard, "pattern", wildcard_pattern, 0);
    rb_define_method(cWildCard, "to_s", wildcard_pattern, 0);
    rb_define_method(cWildCard, "flags", wildcard_flags, 0);
    rb_define_method(cWildCard, "match", wildcard_match, 1);
    rb_define_method(cWildCard, "===", wildcard_match, 1);
    rb_define_const(cWildCard, "NOESCAPE", INT2NUM(FNM_NOESCAPE));
    rb_define_const(cWildCard, "PATHNAME", INT2NUM(FNM_PATHNAME));
    rb_define_const(cWildCard, "PERIOD", INT2NUM(FNM_PERIOD));

    eWildCardError = rb_define_class_under(cWildCard, "Error", rb_eStandardError);
}

