# Copyright (c) 2018-2020, NVIDIA CORPORATION.  All rights reserved.
#
# NVIDIA CORPORATION and its licensors retain all intellectual property
# and proprietary rights in and to this software, related documentation
# and any modifications thereto.  Any use, reproduction, disclosure or
# distribution of this software and related documentation without an express
# license agreement from NVIDIA CORPORATION is strictly prohibited.
#
from omni.kit.search_core import AbstractSearchItem
from omni.kit.search_core import AbstractSearchModel
import asyncio
import carb
import functools
import omni.client
import traceback


def handle_exception(func):
    """
    Decorator to print exception in async functions
    """

    @functools.wraps(func)
    async def wrapper(*args, **kwargs):
        try:
            return await func(*args, **kwargs)
        except asyncio.CancelledError:
            pass
        except Exception as e:
            carb.log_error(f"Exception when async '{func}'")
            carb.log_error(f"{e}")
            carb.log_error(f"{traceback.format_exc()}")

    return wrapper


class SearchItem(AbstractSearchItem):
    def __init__(self, dir_path, file_entry):
        super().__init__()
        self._dir_path = dir_path
        self._file_entry = file_entry

    @property
    def path(self):
        return f"{self._dir_path}/{self._file_entry.relative_path}"

    @property
    def name(self):
        return str(self._file_entry.relative_path)

    @property
    def date(self):
        # TODO: Grid View needs datatime, but Tree View needs a string. We need to make them the same.
        return self._file_entry.modified_time

    @property
    def size(self):
        # TODO: Grid View needs int, but Tree View needs a string. We need to make them the same.
        return self._file_entry.size

    @property
    def is_folder(self):
        return (self._file_entry.flags & omni.client.ItemFlags.CAN_HAVE_CHILDREN) > 0


class SearchModel(AbstractSearchModel):
    def __init__(self, **kwargs):
        super().__init__()
        self._search_text = kwargs.get("search_text", None)
        self._current_dir = kwargs.get("current_dir", None)
        self._search_lifetime = kwargs.get("search_lifetime", None)

        # omni.client doesn't understand my-computer
        if self._current_dir.startswith("my-computer://"):
            self._current_dir = self._current_dir[len("my-computer://"):]

        self.__list_task = asyncio.ensure_future(self.__list())
        self.__items = []

    def destroy(self):
        if not self.__list_task.done():
            self.__list_task.cancel()
        self._search_lifetime = None

    @property
    def items(self):
        return self.__items

    @handle_exception
    async def __list(self):
        result, entries = await omni.client.list_async(self._current_dir)

        if result != omni.client.Result.OK:
            self.__items = []
        else:
            self.__items = [
                SearchItem(self._current_dir, entry) for entry in entries if self._search_text in entry.relative_path
            ]

        self._item_changed()
        self._search_lifetime = None
