# fmt: off
# Auto-generated by https://github.com/mos9527/UnityPyTypetreeCodegen
from typing import List, Union, Optional, TypeVar
from UnityPy.files.ObjectReader import ObjectReader
from UnityPy.classes import *
from UnityPy.classes.math import (ColorRGBA, Matrix3x4f, Matrix4x4f, Quaternionf, Vector2f, Vector3f, Vector4f, float3, float4,)
T = TypeVar("T")
UTTCG_Classes = dict()
def UTTCGen(fullname: str, typetree: dict):
    """dataclass-like decorator for typetree classess with nested type support
    
    limitations:
    - the behavior is similar to slotted dataclasses where shared attributes are inherited
      but allows ommiting init of the parent if kwargs are not sufficient
    - generally supports nested types, however untested and could be slow	
    - and ofc, zero type checking and safeguards :/	
    """    
    REFERENCED_ARGS = {'object_reader'}
    def __inner(clazz: T) -> T:
        # Allow these to be propogated to the props
        def __init__(self, **d):        
            def reduce_init(clazz, **d):
                types : dict = clazz.__annotations__
                for k, sub in types.items():
                    if type(sub) == str:
                        sub = eval(sub) # attrs turns these into strings...why?
                    while sub.__name__ == "Optional":
                        sub = sub.__args__[0]  # Reduce Optional[T] -> T
                    reduce_arg = getattr(sub, "__args__", [None])[0]
                    if k in REFERENCED_ARGS: # Directly refcounted
                        reduce_arg = sub = lambda x: x                         
                    if isinstance(d[k], list):
                        if hasattr(reduce_arg, "__annotations__"):
                            setattr(self, k, [reduce_arg(**x) for x in d[k]])
                        else:
                            setattr(self, k, [reduce_arg(x) for x in d[k]])
                    elif isinstance(d[k], dict) and hasattr(sub, "__annotations__"):
                        setattr(self, k, sub(**d[k]))
                    else:
                        if isinstance(d[k], dict):
                            setattr(self, k, sub(**d[k]))
                        else:
                            setattr(self, k, sub(d[k]))
            def reduce_base(clazz, **d):	
                for __base__ in clazz.__bases__:
                    if hasattr(__base__, "__annotations__"):
                        types : dict = __base__.__annotations__
                        args = {k:d[k] for k in types if k in d}
                        if len(args) == len(types):
                            super(clazz, self).__init__(**args)
                            reduce_init(__base__, **d)                       
                    reduce_base(__base__, **d)
            reduce_base(clazz, **d)               
            reduce_init(clazz, **d)            
        def __repr__(self) -> str:
            return f"{clazz.__name__}({', '.join([f'{k}={getattr(self, k)!r}' for k in self.__annotations__])})"
        def __save(self):
            self.object_reader.save_typetree(self, self.__typetree__)
        clazz.__init__ = __init__
        clazz.__repr__ = __repr__
        clazz.__typetree__ = typetree
        clazz.save = __save
        UTTCG_Classes[fullname] = clazz
        return clazz
    return __inner

# Helper functions

def UTTCGen_AsInstance(src: MonoBehaviour | ObjectReader, fullname: str = None) -> T:
    """Instantiate a class from the typetree definition and the raw data.

    In most cases, this is the function you want to use.
    It will read the typetree data from the MonoBehaviour instance and instantiate the class with the data.

    Args:
        src (MonoBehaviour | ObjectReader): The MonoBehaviour instance or ObjectReader to read from.
        fullname (str): The full name of the class to read. If None, it will be read from the MonoBehaviour instance's `m_Script` property.

    Returns:
        T: An instance of the class defined by the typetree.
    """
    if not fullname and isinstance(src, MonoBehaviour):
        script = src.m_Script.read()
        fullname = script.m_ClassName
        if script.m_Namespace:
            fullname = f"{script.m_Namespace}.{fullname}"    
    clazz = UTTCG_Classes.get(fullname, None)
    assert clazz is not None, f"Class definition for {fullname} not found"    
    if isinstance(src, MonoBehaviour):
        src = src.object_reader
    raw_def = src.read_typetree(clazz.__typetree__, check_read=False)
    instance = clazz(object_reader=src, **raw_def)
    return instance
