# Copyright (c) 2023 Yuki Kimoto
# MIT License

class Native::MethodCall : pointer {
  version_from SPVM;

  use Native::Env;
  use Native::Stack;
  use Native::Method;
  use Callback;
  use Native::MethodCall::Error::ExceptionThrown;
  
  # Fields
  has env : Native::Env;
  
  has stack : Native::Stack;
  
  has method : Native::Method;
  
  # Class Methods
  native static method new_class_method_with_env_stack : Native::MethodCall ($env : Native::Env, $stack : Native::Stack, $basic_type_name : string, $method_name : string);
  
  native static method new_instance_method_static_with_env_stack : Native::MethodCall ($env : Native::Env, $stack : Native::Stack, $basic_type_name : string, $method_name : string);
  
  native static method new_instance_method_with_env_stack : Native::MethodCall ($env : Native::Env, $stack : Native::Stack, $instance : object, $method_name : string);
  
  static method new_class_method : Native::MethodCall ($basic_type_name : string, $method_name : string) {
    return &new_class_method_with_env_stack(undef, undef, $basic_type_name, $method_name);
  }
  
  static method new_instance_method_static : Native::MethodCall ($basic_type_name : string, $method_name : string) {
    return &new_instance_method_static_with_env_stack(undef, undef, $basic_type_name, $method_name);
  }
  
  static method new_instance_method : Native::MethodCall ($instance : object, $method_name : string) {
    return &new_instance_method_with_env_stack(undef, undef, $instance, $method_name);
  }
  
  # Instance Methods
  native method call : object ($args : object[] = undef, $error_id_ref : int[] = undef);
  
  static method call_class_method : object ($basic_type_name : string, $method_name : string, $args : object[] = undef, $error_id_ref : int[] = undef) {
    
    my $class_method = &new_class_method($basic_type_name, $method_name);
    
    my $return_value = $class_method->call($args, $error_id_ref);
    
    return $return_value;
  }
  
  static method call_instance_method_static : object ($basic_type_name : string, $method_name : string, $args : object[] = undef, $error_id_ref : int[] = undef) {
    
    my $instance_method = &new_instance_method_static($basic_type_name, $method_name);
    
    my $return_value = $instance_method->call($args, $error_id_ref);
    
    return $return_value;
  }
  
  static method call_instance_method : object ($method_name : string, $args : object[] = undef, $error_id_ref : int[] = undef) {
    
    my $instance_method = &new_instance_method($args->[0], $method_name);
    
    my $return_value = $instance_method->call($args, $error_id_ref);
    
    return $return_value;
  }
  
  native method get_exception : string ();
  
  native method set_exception : void ($exception : string);
  
  static method new_proto_with_method_name : object ($proto : object, $method_name : string, $args : object[] = undef, $error_id_ref : int[] = undef) {
    
    unless ($proto) {
      die "The prototype object \$proto must be defined.";
    }
    
    my $class_name = type_name $proto;
    
    my $object = Native::MethodCall->call_class_method($class_name, $method_name, $args, $error_id_ref);
    
    return $object;
  }
  
  static method new_proto : object ($proto : object, $args : object[] = undef, $error_id_ref : int[] = undef) {
    
    my $method_name = "new";
    
    my $ret = &new_proto_with_method_name($proto, $method_name, $args, $error_id_ref);
    
    return $ret;
  }
  
}