# Copyright (c) 2004-2020 Adam Karpierz
# Licensed under proprietary License
# Please refer to the accompanying LICENSE file.

from typing import Optional

from public import public
import jni
from .lib import obj
from .lib import cached

from .jframe  import JFrame
from .jstring import JString


@public
class JObjectBase(obj):

    """ObjectBase"""

    __slots__ = ('_jobj', '_own')

    # self._jobj: jni.jobject

    def __init__(self, jenv: jni.JNIEnv, jobj: jni.jobject, own: bool=True):
        self._jobj = jni.NULL
        self._own  = own
        if not jobj:
            from .jconstants import EStatusCode
            from .jvm        import JVMException
            raise JVMException(EStatusCode.UNKNOWN, "Allocating null Object")
        self._jobj = jni.cast(jenv.NewGlobalRef(jobj) if own else jobj, jni.jobject)

    def __del__(self):
        if not self._own or not self.jvm: return
        try: jvm, jenv = self.jvm
        except: return  # pragma: no cover
        if jvm.jnijvm:
            jenv.DeleteGlobalRef(self._jobj)

    handle = property(lambda self: self._jobj)

    def __hash__(self):
        return int(self.hashCode())

    def __eq__(self, other):

        if self is other:
            return True

        if not isinstance(other, JObjectBase):
            return NotImplemented

        self_handle  = self._jobj
        other_handle = other.handle

        if self_handle == other_handle:
            return True  # pragma: no cover

        if self.hashCode() != other.hashCode():
            return False

        with self.jvm as (jvm, jenv):
            return jenv.IsSameObject(self_handle, other_handle)

    def __ne__(self, other):
        eq = self.__eq__(other)
        return NotImplemented if eq is NotImplemented else not eq

    def __str__(self):
        return self.toString()

    @cached
    def getClass(self) -> 'JClass':

        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jcls = jenv.GetObjectClass(self._jobj)
            return self.jvm.JClass(jenv, jcls)

    @cached
    def hashCode(self) -> int:

        with self.jvm as (jvm, jenv):
            return int(jenv.CallIntMethod(self._jobj, jvm.Object.hashCode))

    @cached
    def toString(self) -> Optional[str]:

        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jstr = jenv.CallObjectMethod(self._jobj, jvm.Object.toString)
            return JString(jenv, jstr, own=False).str if jstr else None

    def equals(self, other) -> bool:

        if self is other:
            return True

        if not isinstance(other, JObjectBase):
            return False

        self_handle  = self._jobj
        other_handle = other.handle

        if self_handle == other_handle:
            return True  # pragma: no cover

        with self.jvm as (jvm, jenv):
            jargs = jni.new_array(jni.jvalue, 1)
            jargs[0].l = other_handle
            return (jenv.IsSameObject(self_handle, other_handle) or
                    jenv.CallBooleanMethod(self_handle, jvm.Object.equals, jargs))
