package SPVM::StringBuffer : precompile {
  use SPVM::ArrayUtil (copy_array_range_byte);

  has value : byte[];
  has length : ro int;
  
  sub new : SPVM::StringBuffer () {
    my $self = new SPVM::StringBuffer;
    my $default_capacity = DEFAULT_CAPACITY();
    $self->{value} = new byte [$default_capacity];
    $self->{length} = 0;
    return $self;
  }
  
  sub push : void ($self : self, $string : string) {
    my $length = length($string);
    my $capacity = @$self->{value};
    if ($self->{length} + $length > $capacity) {
      my $new_capacity : int;
      if ($self->{length} + $length > $capacity * 2) {
        $new_capacity = $self->{length} + $length;
      } else {
        $new_capacity = $capacity * 2;
      }
      $self->_reallocate($new_capacity);
    }
    for (my $i = 0; $i < $length; ++$i) {
      $self->{value}[$self->{length} + $i] = $string->[$i];
    }
    $self->{length} += $length;
  }

  sub push_char : void ($self : self, $char : byte) {
    my $capacity = @$self->{value};
    if ($self->{length} + 1 > $capacity) {
      my $new_capacity = $capacity * 2;
      $self->_reallocate($new_capacity);
    }
    $self->{value}[$self->{length}++] = $char;
  }

  sub to_string : string ($self : self) {
    return (string)(copy_array_range_byte($self->{value}, 0, $self->{length}));
  }

  private enum {
    DEFAULT_CAPACITY = 16,
  }
  
  # O($new_capacity)
  private sub _reallocate : void ($self : self, $new_capacity : int) {
    my $new_string = new byte [$new_capacity];
    for (my $i = 0; $i < $self->{length}; ++$i) {
      $new_string->[$i] = $self->{value}[$i];
    }
    $self->{value} = $new_string;
  }
}