#include <ruby.h>
#include <registry.h>
#include "rbkey.h"
#include "rbregistry.h"

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* reg_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 = reg_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 = reg_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 reg_get_type( VALUE class, VALUE kname ) {
  Key key;
  char *name = STR2CSTR( kname );
  VALUE rv;

  keyInit( &key );
  keySetName( &key, name );
  registryGetKey( &key );
  rv = key_get_type( &key );
  return rv;
}

static VALUE reg_get_value( VALUE class, VALUE kname ) {
  Key key;
  char *name = STR2CSTR( kname );
  int errno;
  VALUE rv;

  keyInit( &key );
  keySetName( &key, name );
  
  errno = registryGetKey( &key );
  if (errno == -1) return Qnil;
  rv = get_key_value( &key );
  keyClose( &key );
  return rv;
}


static VALUE reg_set_value( VALUE self, VALUE key, VALUE value ) {
  Key ckey;
  char *name = STR2CSTR( key );
  u_int8_t type;

  keyInit( &ckey );
  keySetName( &ckey, name );
  registryGetKey( &ckey );

  if (set_key_value( &ckey, STR2CSTR( value ) ) == 0) return Qnil;

  registrySetKey( &ckey );
  return value;
}


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


static VALUE get_key( VALUE self, VALUE arg ) {
  Key *key;
  VALUE rbkey;
  u_int8_t type;

  if (TYPE(arg) == T_STRING) {
    rbkey = key_new( cVariable );
    Data_Get_Struct( rbkey, Key, key );
    keySetName( key, STR2CSTR( arg ) );
    keySetType( key, RG_KEY_TYPE_STRING );
  } else {
    Data_Get_Struct( arg, Key, key );
  }
  registryGetKey( key );
  type = keyGetType( key );
  switch( type ) {
    case RG_KEY_TYPE_STRING:
      return Data_Wrap_Struct( cVariable, 0, key_close, key );
      break;
    case RG_KEY_TYPE_BINARY:
      return Data_Wrap_Struct( cVariable, 0, key_close, key );
      break;
    case RG_KEY_TYPE_LINK:
      return Data_Wrap_Struct( cLink, 0, key_close, key );
      break;
    case RG_KEY_TYPE_DIR:
      return Data_Wrap_Struct( cDirectory, 0, key_close, key );
      break;
    case RG_KEY_TYPE_UNDEFINED:
    default:
      return Qnil;
      break;
  }
}

static VALUE set_key( VALUE self, VALUE key ) {
}


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", reg_get_value, 1 );
  rb_define_module_function( mRegistry, "[]", reg_get_value, 1 );
  rb_define_module_function( mRegistry, "set", reg_set_value, 2 );
  rb_define_module_function( mRegistry, "[]=", reg_set_value, 2 );
  rb_define_module_function( mRegistry, "type", reg_get_type, 1 );
  rb_define_module_function( mRegistry, "remove", registry_remove, 1 );
  rb_define_module_function( mRegistry, "keys", get_keys, -1 );

  rb_define_module_function( mRegistry, "get_key", get_key, 1 );
  rb_define_module_function( mRegistry, "set_key", set_key, 1 );

  cKey = rb_define_class_under( mRegistry, "Key", rb_cObject );
  rb_define_method( cKey, "name", get_name, 0 );
  rb_define_method( cKey, "name=", set_name, 1 );
  rb_define_method( cKey, "owner", get_owner, 0 );
  rb_define_method( cKey, "owner=", set_owner, 1 );
  rb_define_method( cKey, "access", get_access, 0 );
  rb_define_method( cKey, "access=", set_access, 1 );
  rb_define_method( cKey, "type", get_type, 0 );
  rb_define_method( cKey, "user?", is_user, 0 );
  rb_define_method( cKey, "system?", is_system, 0 );
  rb_define_method( cKey, "mtime", mtime, 0 );
  rb_define_method( cKey, "ctime", ctime, 0 );
  rb_define_method( cKey, "atime", atime, 0 );
  rb_define_singleton_method( cKey, "new", key_new, 0);

  cLink = rb_define_class_under( mRegistry, "Link", cKey );
  rb_define_method( cLink, "key", get_value, 0 );
  rb_define_method( cLink, "key=", set_value, 1 );

  cDirectory = rb_define_class_under( mRegistry, "Directory", cKey );

  cVariable = rb_define_class_under( mRegistry, "Variable", cKey );
  rb_define_method( cVariable, "value", get_value, 0 );
  rb_define_method( cVariable, "value=", set_value, 1 );
  rb_define_method( cVariable, "comment", get_comment, 0 );
  rb_define_method( cVariable, "comment=", set_comment, 1 );
  rb_define_method( cVariable, "type=", set_type, 0 );

}
