# Copyright (c) 2023 Yuki Kimoto
# MIT License

class Native {
  version_from SPVM;

  use Native::Env;
  use Native::Stack;
  use Native::Runtime;
  use Native::Compiler;
  use StringList;
  
  # Class methods
  native static method get_current_env : Native::Env ();
  
  native static method get_current_stack : Native::Stack ();
  
  static method get_current_runtime : Native::Runtime () {
    
    my $current_env = Native->get_current_env;
    
    my $current_stack = Native->get_current_stack;
    
    my $current_runtime = $current_env->runtime;
    
    return $current_runtime;
  }
  
  static method get_current_compiler : Native::Compiler () {
    
    my $current_runtime = &get_current_runtime;
    
    my $current_compiler = $current_runtime->get_compiler;
    
    return $current_compiler;
  }
  
  native static method check_bootstrap_method : void ($basic_type_name : string);
  
  static method use : void ($class_name : string, $file : string = undef, $line : int = -1) {
    
    my $compiler = Native->get_current_compiler;
    
    $file //= __FILE__;
    $compiler->set_start_file($file);
    $compiler->set_start_line($line > -1 ? $line : __LINE__ + 1);
    $compiler->compile($class_name);
  }
  
  static method use_anon_class : string ($source : string, $file : string = undef, $line : int = -1) {
    
    my $compiler = Native->get_current_compiler;
    
    $file //= __FILE__;
    $compiler->set_start_file($file);
    $compiler->set_start_line($line > -1 ? $line : __LINE__ + 1);
    my $anon_class_name = $compiler->compile_anon_class($source);
    
    return $anon_class_name;
  }
  
  static method eval : object ($eval_source : string, $file : string = undef, $line : int = -1) {
    
    unless ($eval_source) {
      die "The eval source \$eval_source must be defined.";
    }
    
    my $source = "
class {
static method eval : object () {
#line 1
$eval_source
}
}
";
    
    my $compiler = Native->get_current_compiler;
    
    $file //= __FILE__;
    $compiler->set_start_file($file);
    $compiler->set_start_line($line > -1 ? $line : __LINE__ + 1);
    my $anon_class_name = $compiler->compile_anon_class($source);
    
    my $ret = Native::MethodCall->call_class_method($anon_class_name, "eval");
    
    return $ret;
  }
  
  static method push_inc : void ($include_dir : string) {
    
    my $compiler = Native->get_current_compiler;
    
    $compiler->add_include_dir($include_dir);
  }
  
  static method unshift_inc : void ($include_dir : string) {
    
    my $compiler = Native->get_current_compiler;
    
    $compiler->prepend_include_dir($include_dir);
  }
  
  static method inc : string[] () {
    
    my $compiler = Native->get_current_compiler;
    
    my $length = $compiler->get_include_dirs_length;
    
    my $include_dirs_list = StringList->new;
    for (my $i = 0; $i < $length; $i++) {
      my $include_dir = $compiler->get_include_dir($i);
      $include_dirs_list->push($include_dir);
    }
    
    return $include_dirs_list->to_array;
  }
  
  static method set_inc : void ($include_dirs : string[]) {
    
    unless ($include_dirs) {
      die "The include directory \$include_dirs must be defined.";
    }
    
    my $compiler = Native->get_current_compiler;
    
    $compiler->clear_include_dirs;
    
    for my $_ (@$include_dirs) {
      $compiler->add_include_dir($_);
    }
  }
  
}