# Copyright (c) 2023 Yuki Kimoto
# MIT License

class Native::Runtime : pointer {
  version_from SPVM;

  allow Native;
  allow Native::Compiler;
  
  use Native::Compiler;
  use Native::Env;
  use Native::Stack;
  use Native::BasicType;
  use Native::Method;
  use Native::Arg;
  use Native::ClassVar;
  use Native::Field;
  
  use Fn;
  
  # Fields
  has compiler : Native::Compiler;
  
  has env : Native::Env;
  
  # Class Methods
  private static method new_with_pointer : Native::Runtime ($pointer : Address, $options : object[] = undef) {
    
    my $self = new Native::Runtime;
    
    Fn->set_pointer($self, $pointer);
    
    return $self;
  }
  
  # Instance Methods
  native method get_basic_types_length : int ();
  
  native method get_basic_type_by_id : Native::BasicType ($id : int);
  
  native method get_basic_type_by_name : Native::BasicType ($name : string);
  
  native method build_precompile_class_source : string ($basic_type : Native::BasicType);
  
  native method build_precompile_method_source : string ($method : Native::Method);
  
  native method get_compiler : Native::Compiler ();
  
  method get_method_by_name : Native::Method ($class_name : string, $method_name : string) {
    
    my $basic_type = $self->get_basic_type_by_name($class_name);
    
    my $method = $basic_type->get_method_by_name($method_name);
    
    return $method;
  }
  
  method get_field_by_name : Native::Field ($class_name : string, $field_name : string) {
    
    my $basic_type = $self->get_basic_type_by_name($class_name);
    
    my $field = $basic_type->get_field_by_name($field_name);
    
    return $field;
  }
  
  method get_class_var_by_name : Native::ClassVar ($class_name : string, $class_var_name : string) {
    
    my $basic_type = $self->get_basic_type_by_name($class_name);
    
    my $class_var = $basic_type->get_class_var_by_name($class_var_name);
    
    return $class_var;
  }
  
  method get_basic_types : Native::BasicType[] ($options : object[] = undef) {
    
    my $options_h = Hash->new($options);
    
    my $basic_types_length = $self->get_basic_types_length;
    
    my $basic_types_list = List->new(new Native::BasicType[0]);
    
    my $match = 1;
    
    for (my $id = 0; $id < $basic_types_length; $id++) {
      my $basic_type = $self->get_basic_type_by_id($id);
      
      if ($options_h->exists("category")) {
        my $categories = (int[])$options_h->get("category");
        
        my $category_match = 0;
        for my $category (@$categories) {
          if ($basic_type->get_category == $category) {
            $category_match = 1;
            last;
          }
        }
        
        unless ($category_match) {
          next;
        }
      }
      
      if ($options_h->exists("is_anon")) {
        my $is_anon = $options_h->{"is_anon"}->(int);
        
        my $basic_type_name = $basic_type->get_name;
        my $basic_type_is_anon = $basic_type->is_anon;
        
        my $is_anon_match = !!$basic_type->is_anon == !!$is_anon;
        
        unless ($is_anon_match) {
          next;
        }
      }
      
      $basic_types_list->push($basic_type);
    }
    
    my $basic_types = (Native::BasicType[])$basic_types_list->to_array;
    
    return $basic_types;
  }
  
  method get_env : Native::Env () {
    return $self->{env};
  }
}
