class TestCase::Callback {
  use TestCase::Minimal;
  use TestCase::Callback::Callback;
  use TestCase::Callback::ImplementCallback1;
  use TestCase::Callback::ImplementCallback2;
  use Comparator;
  use Stringer;
  use Point;
  
  our $var_prec : int;
  INIT {
    $var_prec = 5;
  }

  static method capture : int () {
    my $capture1 = 7;
    my $capture2 = 10;
    my $cb_obj = [$capture1 : int, $capture2 : int] method : int () {
      
      my $total = $capture1 + $capture2;
      
      return $total;
    };
    
    my $total = $cb_obj->();
    unless ($total == 17) {
      return 0;
    }
    
    return 1;
  }

  static method capture_var_high_precidence_than_class_var : int () {
  
    my $var_prec = 13;
  
    my $cb = [$var_prec : int] method : int () {

      return $var_prec;
    };
    
    unless ($cb->() == 13) {
      return 0;
    }
    
    return 1;
  }
  
  static method sort_objects : void ($values : object[], $comparator : Comparator) {

    my $change_cnt = @$values - 1;
    while( $change_cnt > 0){
      for (my $i = 0; $i < $change_cnt; $i++) {
        my $ret = $comparator->($values->[$i], $values->[$i + 1]);
        
        if ($comparator->($values->[$i], $values->[$i + 1]) == 1) {
          my $tmp_value = $values->[$i];
          $values->[$i] = $values->[$i + 1];
          $values->[$i + 1] = $tmp_value;
        }
      }
      $change_cnt--;
    }
  }
  
  static method comparator : int () {
      my $comparator = method : int ($object1 : object, $object2 : object) {
        my $minimal1 = (TestCase::Minimal)$object1;
        my $minimal2 = (TestCase::Minimal)$object2;
        
        my $x1 = $minimal1->{x};
        my $x2 = $minimal2->{x};
        
        if ($x1 > $x2) {
          return 1;
        }
        elsif ($x1 < $x2) {
          return -1;
        }
        else {
          return 0;
        }
      };
      
      my $minimals = new TestCase::Minimal[3];
      $minimals->[0] = TestCase::Minimal->new;
      $minimals->[0]{x} = 3;
      $minimals->[1] = TestCase::Minimal->new;
      $minimals->[1]{x} = 1;
      $minimals->[2] = TestCase::Minimal->new;
      $minimals->[2]{x} = 2;
      
      TestCase::Callback->sort_objects($minimals, $comparator);
      
      if ($minimals->[0]{x} == 1 && $minimals->[1]{x} == 2 && $minimals->[2]{x} == 3) {
        return 1;
      }
      
      return 0;
  }

  static method basic : int () {
    my $object : object = TestCase::Minimal->new;
    my $implement_callback1 : TestCase::Callback::Callback = TestCase::Callback::ImplementCallback1->new;
    my $implement_callback2 : TestCase::Callback::Callback = TestCase::Callback::ImplementCallback2->new;
    
    my $num1 = $implement_callback1->(0, 0);
    my $num2 = $implement_callback2->(0, 0);

    my $minimal = (TestCase::Minimal)$object;
    $minimal->{x} = 4;
    
    if ($num1 == 1) {
      if ($num2 == 2) {
        if ($minimal->{x} == 4) {
          return 1;
        }
      }
    }
    
    
  }

  static method callback_array : int () {

    {
      my $stringers : Stringer[] = new Stringer[3];
    }
    
    {
      my $cb = method : string ($object : object) {
        my $point = (Point)$object;
        return $point->to_string;
      };
      my $stringers : Stringer[] = [$cb];
      
      unless ($stringers->[0]->(Point->new_xy(1, 2)) eq "(1,2)") {
        return 0;
      }
    }

    {
      my $cb = method : string ($object : object) {
        my $point = (Point)$object;
        return $point->to_string;
      };
      my $stringers : Stringer[] = [(Stringer)$cb];
      
      unless ($stringers->[0]->(Point->new_xy(1, 2)) eq "(1,2)") {
        return 0;
      }
    }
    
    {
      my $stringers : Stringer[] = undef;
    }
    
    # Multi-dimensional array
    {
      my $cb = method : string ($object : object) {
        my $point = (Point)$object;
        return $point->to_string;
      };
      my $stringers_2dim : Stringer[][] = [[$cb]];
      unless ($stringers_2dim->[0][0]->(Point->new_xy(1, 2)) eq "(1,2)") {
        return 0;
      }
    }
    
    return 1;
  }
}