class TestCase::Module::Native::Compiler {
  use Native::Compiler;
  use Native;
  use Native::MethodCall;
  use Stringable;
  use Fn;
  
  static method compile : int () {
    
    # Extra
    {
      my $current_env = Native->get_current_env;
      
      my $current_stack = Native->get_current_stack;
      
      my $current_runtime = $current_env->runtime;
    }
    
    my $current_compiler = Native->get_current_compiler;
    
    my $include_dir = "t/test_add_class";
    
    $current_compiler->prepend_include_dir($include_dir);
    
    $current_compiler->set_start_file(__FILE__);
    
    {
      my $basic_type_name = "MyPoint";
      $current_compiler->set_start_line(__LINE__ + 1);
      Native->use($basic_type_name);
      $current_compiler->compile($basic_type_name);
    }
    
    {
      my $class_method_call = Native::MethodCall->new_class_method("MyPoint", "new");
      
      {
        my $stringable = (Stringable)$class_method_call->call([(object)1, 2]);
        
        unless ($stringable->to_string eq "(1,2)") {
          return 0;
        }
      }
    }
    
    {
      eval { Native::MethodCall->new_class_method("MyPoint", "not_exists"); }
      
      Fn->contains($@, "he \"not_exists\" method in the \"MyPoint\" class cannot be found.");
      
      
    }
    
    {
      my $basic_type_name = "NotExist::XXXXXXXXXXXXXXXXXXXXXXXXX";
      $current_compiler->set_start_line(__LINE__ + 1);
      eval { $current_compiler->compile($basic_type_name); }
      
      unless (Fn->contains($@, "[Compile Error]")) {
        return 0;
      }
      
      unless (Fn->contains($@, "NotExist::XXXXXXXXXXXXXXXXXXXXXXXXX")) {
        return 0;
      }
    }
    
    {
      my $original_inc = Native->inc;
      
      Native->set_inc(new string[0]);
      
      unless (Array->equals(Native->inc, new string[0])) {
        return 0;
      }
      
      Native->push_inc("foo");
      Native->push_inc("bar");
      Native->unshift_inc("baz");
      
      unless (Array->equals(Native->inc, ["baz", "foo", "bar"])) {
        return 0;
      }
      
      Native->set_inc(["a", "b"]);
      
      unless (Array->equals(Native->inc, ["a", "b"])) {
        return 0;
      }
      
      Native->set_inc($original_inc);
    }
    
    return 1;
  }
  
  static method compile_anon_class : int () {
    
    {
      my $compiler = Native->get_current_compiler;
      
      my $source = <<'EOS';
class {
  static method sum : int ($num1 : int, $num2 : int) {
    return $num1 + $num2;
  }
}
EOS
      $compiler->set_start_file(__FILE__);
      $compiler->set_start_line(__LINE__ + 1);
      my $anon_class_name = (string)undef;
      
      $anon_class_name = $compiler->compile_anon_class($source);
      
      my $ret = Native::MethodCall->call_class_method($anon_class_name, "sum", [(object)1, 2]);
      
      unless ($ret->(Int)->value == 3) {
        return 0;
      }
    }
    
    {
      my $source = <<'EOS';
class {
  static method sum : int ($num1 : int, $num2 : int) {
    return $num1 + $num2;
  }
}
EOS
      my $anon_class_name = Native->use_anon_class($source);
      
      my $ret = Native::MethodCall->call_class_method($anon_class_name, "sum", [(object)1, 2]);
      
      unless ($ret->(Int)->value == 3) {
        return 0;
      }
    }
    
    return 1;
  }
  
  static method eval : int () {
    
    {
      my $compiler = Native->get_current_compiler;
      
      my $source = <<'EOS';
my $total = 1 + 2;
return $total;
EOS
      my $data = Native->eval($source, __FILE__, __LINE__);
      
      unless ($data is_type Int) {
        return 0;
      }
      
      unless ($data->(Int)->value == 3) {
        return 0;
      }
      
    }
    
    {
      my $compiler = Native->get_current_compiler;
      
      my $source = <<'EOS';
my $total = 1 + 2 foo
return $total;
EOS

      eval { my $data = Native->eval($source, __FILE__, __LINE__); }
      
      unless (Fn->contains($@, "[Compile Error]")) {
        return 0;
      }
      
      unless (Fn->contains($@, "1:")) {
        return 0;
      }
    }
    
    return 1;
  }
}
