# Copyright (C) 2023 Thomas Hoppe (h0bB1T). All rights reserved.
#
# Unauthorized copying of this file via any medium is strictly prohibited.
# Proprietary and confidential.

import bpy

from typing import Union, List

from bpy.types import Collection, Object, Material

from .type_properties import SourceInfo, get_element_with_source, store_source_info

from ..utils.dev import log_exception

def __list_of(type: str):
    if type == 'C': return bpy.data.collections
    if type == 'O': return bpy.data.objects
    if type == 'M': return bpy.data.materials


def __get_asset(type: str, file: str, name: str, link: bool) -> Union[Collection, Object, Material, None]:
    """
    Does the lookup for a given asset in current .blend, 
    return in case it was found, but does not load it. None if not found.
    """
    if link:
        # Linked objects don't need this, whether they are duplicated or load again,
        # doesn't make any difference. So we return None here, so in conclusion we just
        # re-import them, it's easier and Blender takes care of this.
        return None
    else:
        # Here we use our own properties, control in type_properties.
        return get_element_with_source(__list_of(type), SourceInfo(file, name))
    

# Some shortcuts.
def get_collection(file: str, name: str, link: bool) -> Union[Collection, None]: return __get_asset('C', file, name, link)
def get_object(file: str, name: str, link: bool) -> Union[Object, None]: return __get_asset('O', file, name, link)
def get_material(file: str, name: str, link: bool) -> Union[Material, None]: return __get_asset('M', file, name, link)


def __load_asset(type: str, file: str, name: str, link: bool, reuse: bool, relative: bool, duplicate_reused: bool) -> Union[Collection, Object, Material, None]:
    """
    Central load function. In case of reuse, first tries to find the copy in the current .blend.
    Otherwise load it via import.
    """
    # If we want reuse an existing one, try this first.
    if reuse:
        # First check if exists.
        res = __get_asset(type, file, name, link)
        if res:
            # If this flag is set, we need to create a duplicate and return it.
            if duplicate_reused:
                res = res.copy()
            return res # We have, return it.
        
    # Ok, we have to load it.
    assets = [] # type: List[Union[Collection, Object, Material]]
    try:
        with bpy.data.libraries.load(file, relative=relative, link=link) as (_, data_to):
            # Load depending on type.
            if type == 'C':
                data_to.collections = [name]
                assets = data_to.collections
            elif type == 'O':
                data_to.objects = [name]
                assets = data_to.objects
            elif type == 'M':
                data_to.materials = [name]
                assets = data_to.materials
    except Exception as e:
        log_exception(e, context_msg='Failed to load asset')

    if assets:
        # Seems to be loaded.
        res = assets[0]

        if type == 'C':
            # Collections have special handling.
            # In case of collection, we do it like asset browser. Don't link the collection to 
            # the scene, but create an collection instance and add this instead.

            # No idea how to get the instance object. Just capture current objects in set.
            prev = set(bpy.data.objects)
            bpy.ops.object.collection_instance_add(collection=res.name, align='WORLD')
            new_objs = set(bpy.data.objects).difference(prev)
            if len(new_objs) != 1:
                return None

            # Goe further with this.                
            res = new_objs.pop()
        else:
            if link:
                # Automatically create override for linked assets.
                if res.library and type != 'M':
                    res.asset_clear()
                    res = res.override_create(remap_local_usages=True)
                    res.asset_clear()
            else:
                # For appended one, add our own source info.
                store_source_info(res, SourceInfo(file, name))

                # Remove asset tag, so export would not create duplicate assets.
                res.asset_clear()

        return res
    
    return None


# Some shortcuts.
def load_collection(file: str, name: str, link: bool, reuse: bool, relative: bool, duplicate_reused: bool = False) -> Collection: return __load_asset('C', file, name, link, reuse, relative, duplicate_reused)
def load_object(file: str, name: str, link: bool, reuse: bool, relative: bool, duplicate_reused: bool = True) -> Object: return __load_asset('O', file, name, link, reuse, relative, duplicate_reused)
def load_material(file: str, name: str, link: bool, reuse: bool, relative: bool, duplicate_reused: bool = False) -> Collection: return __load_asset('M', file, name, link, reuse, relative, duplicate_reused)

