#include <ruby.h>
#include <registry.h>

VALUE mRegistry;

static VALUE registry_open() {
  registryOpen();
  if (rb_block_given_p()) {
    rb_yield( Qnil );
    registryClose();
  }
  return Qnil;
}



static VALUE registry_close() {
  registryClose();
  return Qnil;
}


static char* get_name( Key *key ) {
  int size = keyGetFullNameSize( key );
  int i;
  char *name = ALLOC_N( char, size );
  keyGetFullName( key, name, size );
  return name;
}

static void add_keys( VALUE arry, KeySet *keyset ) {
  Key *ckey = keyset->start;
  char *name;

  while (ckey) {
    name = get_name( ckey );
    rb_ary_push( arry, rb_str_new2( name ) );
    ckey = ckey->next;
  }
}


static VALUE get_keys( int argc, VALUE *argv, VALUE self ) {
  KeySet set;
  Key *key;
  char *name;
  VALUE results = rb_ary_new();

  ksInit( &set );
  
  // Start at the root
  if (argc == 0 || argv[0] == Qnil) {
    registryGetRootKeys( &set );
    key = set.start;
    while (key) {
      KeySet all;
      Key *temp;
      Key *ckey;

      ksInit( &all );
      name = get_name( key );
      rb_ary_push( results, rb_str_new2( name ) );
      registryGetChildKeys( name, &all, RG_O_RECURSIVE | RG_O_STATONLY | RG_O_DIR );

      add_keys( results, &all );
      ksClose( &all );
      key = key->next;
    }
  } else {
    char *start_name = STR2CSTR( argv[0] );
    registryGetChildKeys( start_name, &set, RG_O_RECURSIVE | RG_O_DIR );

    add_keys( results, &set );
  }

  // Clean up
  ksClose( &set );
  return results;
}


static VALUE get_value( VALUE class, VALUE kname ) {
  Key key;
  char *name = STR2CSTR( kname );
  char *data;
  size_t size;
  int errno;
  u_int8_t type;
  VALUE rv;

  keyInit( &key );
  keySetName( &key, name );
  
  errno = registryGetKey( &key );
  if (errno == -1) return Qnil;
  type = keyGetType( &key );
  size = keyGetRecordSize( &key );
  data = malloc( size );

  switch (type) {
    case RG_KEY_TYPE_STRING:
      keyGetString( &key, data, size );
      if (data[0] == 0) rv = Qnil;
      else rv = rb_str_new2( data );
    case RG_KEY_TYPE_UNDEFINED:
    case RG_KEY_TYPE_BINARY:
    case RG_KEY_TYPE_DIR:
    case RG_KEY_TYPE_LINK:
    default:
      rv = Qnil;
      break;
  }

  keyClose( &key );
  free( data );
  return rv;
}


static VALUE set_value( VALUE self, VALUE key, VALUE value ) {
  char *keyname = STR2CSTR( key );
  char *keyvalue = STR2CSTR( value );
  registrySetValue( keyname, keyvalue );
  return Qnil;
}


static VALUE registry_remove( VALUE self, VALUE key ) {
  char *keyname = STR2CSTR( key );
  registryRemove( keyname );
  return Qnil;
}




void Init_registry() {
  mRegistry= rb_define_module( "Registry" );
  rb_define_module_function( mRegistry, "open", registry_open, 0 );
  rb_define_module_function( mRegistry, "close", registry_close, 0 );
  rb_define_module_function( mRegistry, "get", get_value, 1 );
  rb_define_module_function( mRegistry, "set", set_value, 2 );
  rb_define_module_function( mRegistry, "remove", registry_remove, 1 );
  rb_define_module_function( mRegistry, "keys", get_keys, -1 );
}

