from __future__ import annotations
from . import JNISimProcedure
from angr.engines.soot.values import SimSootValue_ThisRef
from .method_calls import CallMethodBase

# pylint: disable=arguments-differ,unused-argument

#
# GetObjectClass
#


class GetObjectClass(JNISimProcedure):
    return_ty = "reference"

    def run(self, ptr_env, obj_):
        obj = self.state.jni_references.lookup(obj_)
        obj_class = self.state.javavm_classloader.get_class(obj.type)
        return self.state.jni_references.create_new_reference(obj_class)


#
# AllocObject
#


class AllocObject(JNISimProcedure):
    return_ty = "reference"

    def run(self, ptr_env, obj_class_):
        obj_class = self.state.jni_references.lookup(obj_class_)
        # make sure class is initialized
        self.state.javavm_classloader.init_class(obj_class)
        # return object reference
        obj = SimSootValue_ThisRef(heap_alloc_id=self.state.javavm_memory.get_new_uuid(), type_=obj_class.name)
        return self.state.jni_references.create_new_reference(obj)


#
# NewObject
#


class NewObject(CallMethodBase):
    return_ty = "reference"
    obj = None
    local_vars = ("obj",)

    def run(self, ptr_env, obj_class_, method_id_):
        # alloc object
        obj_class = self.state.jni_references.lookup(obj_class_)
        self.state.javavm_classloader.init_class(obj_class)
        self.obj = SimSootValue_ThisRef(heap_alloc_id=self.state.javavm_memory.get_new_uuid(), type_=obj_class.name)
        # call constructor
        method_id = self.state.jni_references.lookup(method_id_)
        self._invoke(method_id, self.obj, dynamic_dispatch=False)

    def return_from_invocation(self, ptr_env, obj_class_, method_id_):
        return self.state.jni_references.create_new_reference(self.obj)


#
# IsInstanceOf
#


class IsInstanceOf(CallMethodBase):
    return_ty = "boolean"

    def run(self, ptr_env, obj_, target_class_):
        target_class = self.state.jni_references.lookup(target_class_)
        obj = self.state.jni_references.lookup(obj_)
        obj_class = self.state.javavm_classloader.get_class(obj.type)
        # check if target class equals either the object class or one of its superclasses
        class_hierarchy = self.state.javavm_classloader.get_class_hierarchy(obj_class)
        if target_class in class_hierarchy:
            return self.JNI_TRUE
        return self.JNI_FALSE


#
# IsSameObject
#


class IsSameObject(JNISimProcedure):
    return_ty = "boolean"

    def run(self, ptr_env, ref1_, ref2_):
        ref1 = self.state.jni_references.lookup(ref1_)
        ref2 = self.state.jni_references.lookup(ref2_)
        if ref1 == ref2:
            return self.JNI_TRUE
        return self.JNI_FALSE
