package Qstruct::Array;

use strict;
use Carp;

use Tie::Array;
our @ISA = ('Tie::Array');


sub TIEARRAY {
  my $class = shift;
  my $obj = shift;
  return bless $obj, $class;
}

sub FETCH {
  my $self = shift;
  my $index = shift;
  return $self->{a}->($index);
}

sub FETCHSIZE {
  my $self = shift;
  return $self->{n};
}

sub STORE {
  croak "unable to modify a read-only Qstruct array";
}


package Qstruct::ArrayRef;

use strict;

use overload
  ## Make this behave like an array ref
  '@{}' => sub { $_[0]->{arr} },

  ## This method is used by Test::More is_deeply()
  ## FIXME: if it doesn't match is_deeply() prints dumb 'Operation """": no method found' error
  'eq' => sub {
            my ($self, $compare) = @_;
            my $arr = $self->{arr};
            return '' if @$arr != @$compare;
            for my $i (0 .. (@$compare - 1)) {
              return '' if $arr->[$i] ne $compare->[$i];
            }
            return 1;
          },
  ;


sub new {
  my ($class, $elems, $elem_accessor, $raw_accessor) = @_;

  my @arr;
  tie @arr, 'Qstruct::Array', {
                                n => $elems,
                                a => $elem_accessor,
                              };

  return bless { n => $elems, arr => \@arr, a => $elem_accessor, raw => $raw_accessor, }, $class; 
}


sub raw {
  my $self = shift;

  die "array type doesn't support raw access"
    if !$self->{raw};

  $self->{raw}->(exists $_[0] ? $_[0] : my $o);
  return $o if !exists $_[0];
}


sub foreach {
  my $self = shift;

  for my $i (0 .. ($self->{n} - 1)) {
    $self->{a}->($i, my $o);
    $_[0]->($o);
  }
}


sub len {
  return $_[0]->{n};
}


sub get {
  my $self = shift;
  $self->{a}->($_[0], exists $_[1] ? $_[1] : my $o);
  return $o if !exists $_[1];
}


1;