package TestCase::Lib::SPVM::StringBuffer {
  use SPVM::StringBuffer;

  sub check_fields : int ($buffer : SPVM::StringBuffer, $capacity : int, $length : int) {
    # check_fields fields
    unless ($buffer->capacity == $capacity && $buffer->length == $length) {
      warn("String fields mismatch.\n\t" .
          "got:      (capacity: " . $buffer->capacity . ", length: " . $buffer->length . ")\n\t" .
          "expected: (capacity: " . $capacity . ", length: " . $length . ")");
      return 0;
    }
    return 1;
  }

  sub test_ctor_default : int () {
    unless (check_fields(SPVM::StringBuffer->new, 16, 0)) {
      return 0;
    }
    return 1;
  }

  sub test_new_opt : int () {
    my $opt = SPVM::Hash->new;
    $opt->set_int(capacity => 20000);
    unless (check_fields(SPVM::StringBuffer->new_opt($opt), 20000, 0)) {
      return 0;
    }
    eval {
      my $opt = SPVM::Hash->new;
      $opt->set_int(capacity => 0);
      SPVM::StringBuffer->new_opt($opt);
    };
    unless ($@) {
      return 0;
    }
    $@ = undef;
    eval {
      my $opt = SPVM::Hash->new;
      $opt->set_int(capacity => -1);
      SPVM::StringBuffer->new_opt($opt);
    };
    unless ($@) {
      return 0;
    }
    $@ = undef;
    return 1;
  }

  sub test_length : int () {
    unless (SPVM::StringBuffer->new->length == 0) {
      return 0;
    }
    my $buffer2 = SPVM::StringBuffer->new;
    $buffer2->push("a");
    unless ($buffer2->length == 1) {
      return 0;
    }
    return 1;
  }

  sub test_substr : int () {
    my $buffer = SPVM::StringBuffer->new;
    $buffer->push("abcd");
    unless ($buffer->substr(1, 2) eq "bc") {
      return 0;
    }
    eval {
      my $buffer = SPVM::StringBuffer->new;
      $buffer->push("a");
      $buffer->substr(1, 1);
    };
    unless ($@) {
      return 0;
    }
    eval {
      my $opt = SPVM::Hash->new;
      $opt->set_int(capacity => 1);
      my $buffer = SPVM::StringBuffer->new_opt($opt);
      $buffer->push("a");
      $buffer->substr(0, 2);
    };
    unless ($@) {
      return 0;
    }
    
    $@ = undef;
    
    return 1;
  }

  sub test_substr_copy_on_write : int () {
    {
      my $buffer = SPVM::StringBuffer->new;
      $buffer->push("abc");
      my $temp = (byte[])$buffer->to_string;
      unless (refcnt $temp == 2) { # referenced by field and temp
        return 0;
      }
    }
    {
      my $buffer = SPVM::StringBuffer->new;
      $buffer->push("abc");
      $buffer->substr(1, 1);
      my $temp = (byte[])$buffer->to_string;
      unless (refcnt $temp == 2) {
        warn("substr is used. string is referenced by text's field and temp, but refcnt: " . refcnt $temp);
        return 0;
      }
    }
    {
      my $buffer = SPVM::StringBuffer->new;
      $buffer->push("abc");
      my $buffer_sub = $buffer->substr(1, 1);
      my $temp = (byte[])$buffer_sub;
      unless (refcnt $temp == 3) {
        warn("substr is used and text_sub is created. string is referenced by text's and text_sub fields and temp, but refcnt: " . refcnt $temp);
        return 0;
      }
    }
    return 1;
  }

  sub test_push  : int () {
    my $opt = SPVM::Hash->new;
    $opt->set_int(capacity => 1);
    my $buffer = SPVM::StringBuffer->new_opt($opt);
    $buffer->push ("abc");
    unless ($buffer->to_string eq "abc" && $buffer->capacity == 3) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push ("de");
    unless ($buffer->to_string eq "abcde" && $buffer->capacity == 6) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push ("f");
    unless ($buffer->to_string eq "abcdef" && $buffer->capacity == 6) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    return 1;
  }

  sub test_push_char  : int () {
    my $opt = SPVM::Hash->new;
    $opt->set_int(capacity => 1);
    my $buffer = SPVM::StringBuffer->new_opt($opt);
    $buffer->push_char('a');
    unless ($buffer->to_string eq "a" && $buffer->capacity == 1) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push_char('b');
    unless ($buffer->to_string eq "ab" && $buffer->capacity == 2) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push_char('c');
    unless ($buffer->to_string eq "abc" && $buffer->capacity == 4) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    return 1;
  }

  sub test_push_range  : int () {
    my $opt = SPVM::Hash->new;
    $opt->set_int(capacity => 1);
    my $buffer = SPVM::StringBuffer->new_opt($opt);
    my $string = "abcde";
    $buffer->push_range($string, 0, 3);
    unless ($buffer->to_string eq "abc" && $buffer->capacity == 3) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push_range($string, 1, 3);
    unless ($buffer->to_string eq "abcbcd" && $buffer->capacity == 6) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    $buffer->push_range($string, 2, 3);
    unless ($buffer->to_string eq "abcbcdcde" && $buffer->capacity == 12) {
      warn("actual string: " . $buffer->to_string . ", capacity: " . $buffer->capacity);
      return 0;
    }
    return 1;
  }

  sub test_append_copy_on_write : int () {
    warn("TODO: append copy-on-write test is not implemented yet");
    return 0;
  }

  sub test_to_string : int () {
    {
      my $opt = SPVM::Hash->new;
      $opt->set_int(capacity => 1);
      my $minimal_buf = SPVM::StringBuffer->new_opt($opt);
      $minimal_buf->push ("a");
      unless ($minimal_buf->to_string eq "a") {
        return 0;
      }
    }
    {
      my $opt = SPVM::Hash->new;
      $opt->set_int(capacity => 1000000);
      my $large_buf = SPVM::StringBuffer->new_opt($opt);
      $large_buf->push ("b");
      unless ($large_buf->to_string eq "b") {
        return 0;
      }
    }
    return 1;
  }

  sub test_index : int () {
    my $buf = SPVM::StringBuffer->new;
    $buf->push ("hogehogefuga");
    unless ($buf->index("hoge", 0) == 0) {
      return 0;
    }
    unless ($buf->index("hoge", 1) == 4) {
      return 0;
    }
    unless ($buf->index("fuga", 0) == 8) {
      return 0;
    }
    unless ($buf->index("foo", 0) == -1) {
      return 0;
    }
    return 1;
  }
}