unit uIndexer;

interface

uses Rubies;

var
  cIndexer: Tvalue;

procedure DefineIndexer(klass: Tvalue; name: PChar; reader, writer: TFunc1);
procedure Init_Indexer;

implementation

uses
  uPhi, uConv;

function Indexer_new(This: Tvalue): Tvalue; cdecl;
begin
  result := rb_data_object_alloc(cIndexer, nil, nil, nil);
  rb_iv_set(result, '_owner', This);
  rb_iv_set(This, '_indexer', result);
  rb_iv_set(result, '_name', ap_String(rb_id2name(rb_frame_last_func)));
end;

function Indexer_aref(argc: Integer; argv: Pointer; This: Tvalue): Tvalue; cdecl;
var
  obj: Tvalue;
  vreader: Tvalue;
  reader: TFunc1;
  name:String;
begin
  obj := rb_iv_get(This, '_owner');
  name := dl_String(rb_iv_get(This, '_name'));
  vreader := rb_cv_get(ap_class_of(obj), PChar('@@'+name+'_index_reader'));
  if vreader = Qnil then
    result := Qnil
  else begin
    reader := ap_data_get_struct(vreader);
    result := reader(argc, argv, obj);
  end;
end;

function Indexer_aset(argc: Integer; argv: Pointer; This: Tvalue): Tvalue; cdecl;
var
  obj: Tvalue;
  vwriter: Tvalue;
  writer: TFunc1;
  name:String;
begin
  obj := rb_iv_get(This, '_owner');
  name := dl_String(rb_iv_get(This, '_name'));
  vwriter := rb_cv_get(ap_class_of(obj), PChar('@@'+name+'_index_writer'));
  if vwriter = Qnil then
    result := Qnil
  else begin
    writer := ap_data_get_struct(vwriter);
    result := writer(argc, argv, obj);
  end;
end;

procedure DefineIndexer(klass: Tvalue; name: PChar; reader, writer: TFunc1);
var
  vreader: Tvalue;
  vwriter: Tvalue;
begin
  rb_define_method(klass, name, @Indexer_new, 0);
  if @reader = nil then vreader := Qnil else vreader := rb_data_object_alloc(cIndexer, @reader, nil, nil);
  if @writer = nil then vwriter := Qnil else vwriter := rb_data_object_alloc(cIndexer, @writer, nil, nil);
  rb_cv_set(klass, PChar('@@'+String(name)+'_index_reader'), vreader);
  rb_cv_set(klass, PChar('@@'+String(name)+'_index_writer'), vwriter);
end;

procedure Init_Indexer;
begin
  cIndexer := rb_define_class_under(mPhi, 'Indexer', ap_cObject);
  rb_define_method(cIndexer, '[]', @Indexer_aref, -1);
  rb_define_method(cIndexer, '[]=', @Indexer_aset, -1);
end;

end.
