# Copyright (c) 2004 Adam Karpierz
# SPDX-License-Identifier: CC-BY-NC-ND-4.0 OR LicenseRef-Proprietary
# Please refer to the accompanying LICENSE file.

from __future__ import annotations

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

from .jframe     import JFrame
from .jmember    import JMember
from .jannotated import JAnnotatedElement
from .jstring    import JString
from ._util      import str2jchars


@public
class JField(JMember, JAnnotatedElement):
    """Java Field"""

    __slots__ = ()

    @cached
    def _jfid(self, jenv: jni.JNIEnv) -> jni.jfieldID:
        return jenv.FromReflectedField(self._jobj)

    @cached
    def getType(self) -> JClass:
        """Returns a Class object that identifies the declared type for the field \
        represented by this Field object."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jcls = jenv.CallObjectMethod(self._jobj, jvm.Field.getType)
            return self.jvm.JClass(jenv, jcls)

    @cached
    def isEnumConstant(self) -> bool:
        """Returns True if this field represents an element of an enumerated type; \
        returns False otherwise."""
        with self.jvm as (jvm, jenv):
            return jenv.CallBooleanMethod(self._jobj, jvm.Field.isEnumConstant)

    def getStaticBoolean(self, jcls: JClass) -> bool:
        """Gets the value of a static boolean field."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticBooleanField(jcls.handle, self._jfid(jenv))

    def getStaticChar(self, jcls: JClass) -> str:
        """Gets the value of a static field of type char or of another \
        primitive type convertible to type char via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticCharField(jcls.handle, self._jfid(jenv))

    def getStaticByte(self, jcls: JClass) -> int:
        """Gets the value of a static byte field."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticByteField(jcls.handle, self._jfid(jenv))

    def getStaticShort(self, jcls: JClass) -> int:
        """Gets the value of a static field of type short or of another \
        primitive type convertible to type short via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticShortField(jcls.handle, self._jfid(jenv))

    def getStaticInt(self, jcls: JClass) -> int:
        """Gets the value of a static field of type int or of another \
        primitive type convertible to type int via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticIntField(jcls.handle, self._jfid(jenv))

    def getStaticLong(self, jcls: JClass) -> int:
        """Gets the value of a static field of type long or of another \
        primitive type convertible to type long via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticLongField(jcls.handle, self._jfid(jenv))

    def getStaticFloat(self, jcls: JClass) -> float:
        """Gets the value of a static field of type float or of another \
        primitive type convertible to type float via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticFloatField(jcls.handle, self._jfid(jenv))

    def getStaticDouble(self, jcls: JClass) -> float:
        """Gets the value of a static field of type double or of another \
        primitive type convertible to type double via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetStaticDoubleField(jcls.handle, self._jfid(jenv))

    def getStaticString(self, jcls: JClass) -> str | None:
        """Returns the value of a static field represented by this Field, \
        on the specified class argument, as a String."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jstr = jenv.GetStaticObjectField(jcls.handle, self._jfid(jenv))
            return JString(jenv, jstr, own=False).str if jstr else None

    def getStaticObject(self, jcls: JClass) -> JObject | None:
        """Returns the value of a static field represented by this Field, \
        on the specified class argument, as an Object."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jobj = jenv.GetStaticObjectField(jcls.handle, self._jfid(jenv))
            return self.jvm.JObject(jenv, jobj) if jobj else None

    def setStaticBoolean(self, jcls: JClass, val: bool):
        """Sets the value of a static field as a boolean on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticBooleanField(jcls.handle, self._jfid(jenv), val)

    def setStaticChar(self, jcls: JClass, val: str):
        """Sets the value of a static field as a char on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticCharField(jcls.handle, self._jfid(jenv), val)

    def setStaticByte(self, jcls: JClass, val: int):
        """Sets the value of a static field as a byte on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticByteField(jcls.handle, self._jfid(jenv), val)

    def setStaticShort(self, jcls: JClass, val: int):
        """Sets the value of a static field as a short on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticShortField(jcls.handle, self._jfid(jenv), val)

    def setStaticInt(self, jcls: JClass, val: int):
        """Sets the value of a static field as an int on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticIntField(jcls.handle, self._jfid(jenv), val)

    def setStaticLong(self, jcls: JClass, val: int):
        """Sets the value of a static field as a long on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticLongField(jcls.handle, self._jfid(jenv), val)

    def setStaticFloat(self, jcls: JClass, val: float):
        """Sets the value of a static field as a float on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticFloatField(jcls.handle, self._jfid(jenv), val)

    def setStaticDouble(self, jcls: JClass, val: float):
        """Sets the value of a static field as a double on the specified \
        class."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticDoubleField(jcls.handle, self._jfid(jenv), val)

    def setStaticString(self, jcls: JClass, val: str):
        """Sets the value of a static field represented by this Field, \
        on the specified class argument to the specified new String value."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            if val is None:
                jstr = None
            else:
                jchars, size, jbuf = str2jchars(val)
                jstr = jenv.NewString(jchars, size)
            jenv.SetStaticObjectField(jcls.handle, self._jfid(jenv), jstr)

    def setStaticObject(self, jcls: JClass, val: JObject | None):
        """Sets the value of a static field represented by this Field, \
        on the specified class argument to the specified new Object value."""
        with self.jvm as (jvm, jenv):
            jenv.SetStaticObjectField(jcls.handle, self._jfid(jenv),
                                      val.handle if val is not None else None)

    def getBoolean(self, this: JObject) -> bool:
        """Gets the value of an instance boolean field."""
        with self.jvm as (jvm, jenv):
            return jenv.GetBooleanField(this.handle, self._jfid(jenv))

    def getChar(self, this: JObject) -> str:
        """Gets the value of an instance field of type char or of another \
        primitive type convertible to type char via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetCharField(this.handle, self._jfid(jenv))

    def getByte(self, this: JObject) -> int:
        """Gets the value of an instance byte field."""
        with self.jvm as (jvm, jenv):
            return jenv.GetByteField(this.handle, self._jfid(jenv))

    def getShort(self, this: JObject) -> int:
        """Gets the value of an instance field of type short or of another \
        primitive type convertible to type short via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetShortField(this.handle, self._jfid(jenv))

    def getInt(self, this: JObject) -> int:
        """Gets the value of an instance field of type int or of another \
        primitive type convertible to type int via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetIntField(this.handle, self._jfid(jenv))

    def getLong(self, this: JObject) -> int:
        """Gets the value of an instance field of type long or of another \
        primitive type convertible to type long via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetLongField(this.handle, self._jfid(jenv))

    def getFloat(self, this: JObject) -> float:
        """Gets the value of an instance field of type float or of another \
        primitive type convertible to type float via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetFloatField(this.handle, self._jfid(jenv))

    def getDouble(self, this: JObject) -> float:
        """Gets the value of an instance field of type double or of another \
        primitive type convertible to type double via a widening conversion."""
        with self.jvm as (jvm, jenv):
            return jenv.GetDoubleField(this.handle, self._jfid(jenv))

    def getString(self, this: JObject) -> str | None:
        """Returns the value of an instance field represented by this Field, \
        on the specified object argument, as a String."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jstr = jenv.GetObjectField(this.handle, self._jfid(jenv))
            return JString(jenv, jstr, own=False).str if jstr else None

    def getObject(self, this: JObject) -> JObject | None:
        """Returns the value of an instance field represented by this Field, \
        on the specified object argument, as an Object."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            jobj = jenv.GetObjectField(this.handle, self._jfid(jenv))
            return self.jvm.JObject(jenv, jobj) if jobj else None

    def setBoolean(self, this: JObject, val: bool):
        """Sets the value of an instance field as a boolean on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetBooleanField(this.handle, self._jfid(jenv), val)

    def setChar(self, this: JObject, val: str):
        """Sets the value of an instance field as a char on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetCharField(this.handle, self._jfid(jenv), val)

    def setByte(self, this: JObject, val: int):
        """Sets the value of an instance field as a byte on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetByteField(this.handle, self._jfid(jenv), val)

    def setShort(self, this: JObject, val: int):
        """Sets the value of an instance field as a short on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetShortField(this.handle, self._jfid(jenv), val)

    def setInt(self, this: JObject, val: int):
        """Sets the value of an instance field as an int on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetIntField(this.handle, self._jfid(jenv), val)

    def setLong(self, this: JObject, val: int):
        """Sets the value of an instance field as a long on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetLongField(this.handle, self._jfid(jenv), val)

    def setFloat(self, this: JObject, val: float):
        """Sets the value of an instance field as a float on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetFloatField(this.handle, self._jfid(jenv), val)

    def setDouble(self, this: JObject, val: float):
        """Sets the value of an instance field as a double on the specified \
        object."""
        with self.jvm as (jvm, jenv):
            jenv.SetDoubleField(this.handle, self._jfid(jenv), val)

    def setString(self, this: JObject, val: str | None):
        """Sets the value of an instance field represented by this Field, \
        on the specified object argument to the specified new String value."""
        with self.jvm as (jvm, jenv), JFrame(jenv, 1):
            if val is None:
                jstr = None
            else:
                jchars, size, jbuf = str2jchars(val)
                jstr = jenv.NewString(jchars, size)
            jenv.SetObjectField(this.handle, self._jfid(jenv), jstr)

    def setObject(self, this: JObject, val: JObject | None):
        """Sets the value of an instance field represented by this Field, \
        on the specified object argument to the specified new Object value."""
        with self.jvm as (jvm, jenv):
            jenv.SetObjectField(this.handle, self._jfid(jenv),
                                val.handle if val is not None else None)

    # Helpers

    @cached
    def getSignature(self) -> str:
        """Returns the field signature."""
        return self.getType().getSignature()


from .jclass  import JClass   # noqa: E402
from .jobject import JObject  # noqa: E402
