package TestCase {
  use TestCase::Minimal;
  use TestCase::Simple;
  use SPVM::Util (INT8_MIN, INT8_MAX, INT16_MIN, INT16_MAX, INT32_MIN, INT32_MAX, INT64_MIN, INT64_MAX, FLT_MIN, FLT_MAX, DBL_MIN, DBL_MAX);

  our $PACKAGE_VAR_INT : int;
  our $PACKAGE_VAR_LONG : long;
  our $PACKAGE_VAR_MINIMAL : TestCase::Minimal;

  our $PACKAGE_VAR_INT2 : int;
  our $PACKAGE_VAR_LONG2 : long;
  our $PACKAGE_VAR_MINIMAL2 : TestCase::Minimal;

  has x_byte : rw public byte;
  has x_short : rw public short;
  has x_int : rw public int;
  has x_long : rw public long;
  has x_float : rw public float;
  has x_double : rw public double;
  has x_iarray : rw public int[];
  has x_barray : rw public byte[];
  has x_test_case : rw public TestCase;
  has minimal : rw public TestCase::Minimal;
  has private_field : private int;
  
  sub INT : int () { return 127; }
  sub FLOAT_PRECICE : float () { return 16384.5f; }
  sub DOUBLE_PRECICE : double () { return 65536.5; }

  sub my_exe_test : int ($num : int) {
    return $num * 2;
  }
  sub main : int ($argv : string[]) {
    return 10;
  }
  
  sub get_private_field : int ($self : self) {
    return $self->{private_field};
  }
  
  sub get_x_byte : byte ($self : self) {
    return $self->{x_byte};
  }

  sub get_x_short : short ($self : self) {
    return $self->{x_short};
  }

  sub get_x_int : int ($self : self) {
    return $self->{x_int};
  }

  sub get_x_long : long ($self : self) {
    return $self->{x_long};
  }

  sub get_x_float : float ($self : self) {
    return $self->{x_float};
  }

  sub get_x_double : double ($self : self) {
    return $self->{x_double};
  }
  
  # Only compile check
  sub eval_block_stack_check : void () {
    eval {
      my $obj_error1 = "Error1";
      
      die $obj_error1;
    };
    
    {
      my $obj_error2 = "Error2";
      
      die $obj_error2;
    }
  }
  
  sub new : TestCase () {
    return new TestCase;
  }
  
  sub my_var_in_loop_free : int () {
    for (my $i = 0; $i < 5; $i++) {
      my $minimal = TestCase::Minimal->new;
    }
  }
  
  sub package_var : int () {
    if ($TestCase::PACKAGE_VAR_INT == 0) {
      $TestCase::PACKAGE_VAR_INT = INT32_MAX();
      if ($TestCase::PACKAGE_VAR_INT == INT32_MAX()) {
        $TestCase::PACKAGE_VAR_LONG = INT64_MAX();
        if ($TestCase::PACKAGE_VAR_LONG == INT64_MAX()) {
          $TestCase::PACKAGE_VAR_MINIMAL = TestCase::Minimal->new();
          
          $TestCase::PACKAGE_VAR_MINIMAL->set_x(4);
          
          if ($TestCase::PACKAGE_VAR_MINIMAL->set_x(4)) {
            $TestCase::PACKAGE_VAR_MINIMAL = undef;
            return 1;
          }
        }
      }
    }
    return 0;
  }

  sub package_var_rel_name : int () {
    $PACKAGE_VAR_INT = 2;
    
    if ($PACKAGE_VAR_INT == 2) {
      if ($TestCase::PACKAGE_VAR_INT == 2) {
        $PACKAGE_VAR_INT = 0;
        return 1;
      }
    }
    
    return 0;
  }

  sub package_var_other_package : int () {
    if ($TestCase::Simple::FOO == 0) {
      $TestCase::Simple::FOO = 1;
      if ($TestCase::Simple::FOO == 1) {
        return 1;
      }
    }
    return 0;
  }
  
  sub get_minimal : TestCase::Minimal ($self : self) {
    return $self->{minimal};
  }

  # Resorved word
  sub int : int ($obj : TestCase) {
    
    return 5;
  }
  
  sub use_reserved_word : int () {
    
    my $test_case = new TestCase;
    
    if (TestCase->int($test_case) == 5) {
      if (TestCase->int($test_case) == 5) {
        return 1;
      }
    }
    
    return 0;
  }
  
  sub concat : string () {
    "a" . "b";
    
    my $value = "a" . "b";
    
    return $value;
  }
  
  # string
  sub string_utf8 : string () {
    my $value = "あいうえお";
    
    return $value;
  }
  sub string_empty : string () {
    my $value = "";
    
    return $value;
  }

  sub my_var_scope : int () {
    my $ok1 = 0;
    my $ok2 = 0;
    my $ok3 = 0;
    
    my $var1 = 1;
    my $var2 = 2;
    
    if ($var1 == 1) {
      if ($var2 == 2) {
        $ok1 = 1;
      }
    }
    
    {
      my $var1 = 3;
      my $var2 = 4;
      my $var3 = 5;
      
      if ($var1 == 3) {
        if ($var2 == 4) {
          if ($var3 == 5) {
            $ok2 = 1;
          }
        }
      }
    }
    
    my $var3 = 6;
    
    if ($var1 == 1) {
      if ($var2 == 2) {
        if ($var3 == 6) {
          $ok3 = 1;
        }
      }
    }
    
    if ($ok1) {
      if ($ok2) {
        if ($ok3) {
          return 1;
        }
      }
    }
    
    return 0;
  }
  
  # My variable is initialized zero
  sub my_var_initialized_zero : int () {
    my $var1 : int;
    my $var2 : TestCase;
    
    if ($var1 == 0) {
      if ($var2 == undef) {
        return 1;
      }
    }
    return 0;
  }

  sub my_var_block : int () {
    my $success = 1;
    
    my $var1 = 1;
    if ($var1 != 1) {
      $success = 0;
    }
    
    {
      my $var1 = 2;
      if ($var1 != 2) {
        $success = 0;
      }
      
      {
        my $var1 = $var1;
        if ($var1 != 2) {
          $success = 0;
        }
        $var1 = 3;
      }
      if ($var1 != 2) {
        $success = 0;
      }
    }
    
    if ($success) {
      return 1;
    }
    return 0;
  }
  
  # new near small base_object_max_byte_size_use_memory_pool
  sub new_near_small_base_object_max_byte_size_use_memory_pool : int () {
    my $block = new byte[0xFFF0];
    
    return 1;
  }

  # Sum
  sub sum_byte : byte ($a : byte, $b :byte) {
    
    my $total = (byte)((int)$a + (int)$b);
    
    return $total;
  }

  sub sum_short : short ($a : short, $b :short) {
    
    my $total = (short)((int)$a + (int)$b);
    
    return $total;
  }

  sub sum_int : int ($a : int, $b :int) {
    
    my $total = $a + $b;
    
    return $total;
  }

  sub sum_long : long ($a : long, $b :long) {
    
    my $total = $a + $b;
    
    return $total;
  }

  sub sum_float : float ($a : float, $b :float) {
    
    my $total = $a + $b;
    
    return $total;
  }

  sub sum_double : double ($a : double, $b :double) {
    
    my $total = $a + $b;
    
    return $total;
  }
  
}