# Copyright (c) 2021-2022, 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.
#

__all__ = ["HdEngineRendererMenuFactory"]

from .hd_renderer_plugins import HdRendererPlugins
import carb

_log_issue = carb.log_warn


class HdRenderer:
    def __init__(self, *args):
        self.pluginID, self.displayName, self.usdPlugin = args

    def __repr__(self):
        return f'HdRenderer("{self.pluginID}", "{self.displayName}")'


class HdEngineRenderer:
    def __init__(self, *args):
        self.engineName, self.renderModePath = args
        self.renderers = []

    def __repr__(self):
        return f'HdEngineRenderer("{self.engineName}", "{self.renderModePath}", {[r for r in self.renderers]})'


class HdRendererList:
    RTX_RENDER_MODE_PATH = "/rtx/rendermode"
    IRY_RENDER_MODE_PATH = "/rtx/iray/rendermode"
    PXR_RENDER_MODE_PATH = "/pxr/rendermode"

    @classmethod
    def enabled_engines(cls):
        engines = carb.settings.get_settings().get("/renderer/enabled")
        if engines:
            # Return as an ordered list based on how renderers should display
            engines = engines.split(",")
            for engine in ("rtx", "iray", "index", "pxr"):
                if engine in engines:
                    engines.remove(engine)
                    yield engine
        else:
            engines = ("rtx", "iray", "pxr")

        for engine in engines:
            yield engine

    def __init__(self, update_fn: callable = None, engines=None):
        # Use an OrderedDict to preserve incoming or app-default order when added
        from collections import OrderedDict
        self.__renderers = OrderedDict()
        self.__hd_plugins = None
        self.__updated_fn = update_fn

        if not engines:
            engines = self.enabled_engines()

        for hdEngine in engines:
            if hdEngine == "rtx":
                self.__add_renderer(hdEngine, self.RTX_RENDER_MODE_PATH, "RaytracedLighting", "RTX - Real-Time")
                self.__add_renderer(hdEngine, self.RTX_RENDER_MODE_PATH, "PathTracing", "RTX - Interactive (Path Tracing)")
                if carb.settings.get_settings().get("/rtx-transient/aperture/enabled"):
                    self.__add_renderer(
                        hdEngine, self.RTX_RENDER_MODE_PATH, "LightspeedAperture", "RTX - Aperture (Game Path Tracer)"
                    )
            elif hdEngine == "iray":
                self.__add_renderer(hdEngine, self.IRY_RENDER_MODE_PATH, "iray", "RTX - Accurate (Iray)")
                # No known setting to enable this.
                if False:
                    self.__add_renderer(hdEngine, self.IRY_RENDER_MODE_PATH, "irt", "RTX - Accurate (Iray Interactive)")
            elif hdEngine == "index":
                self.__add_renderer(hdEngine, None, "index", "RTX - Scientific (IndeX)")
            elif hdEngine == "pxr":
                self.__hd_plugins = HdRendererPlugins(self.__add_pxr_renderers)
                self.__add_pxr_renderers(self.__hd_plugins)
            else:
                _log_issue(f"Unknown Hydra engine'{hdEngine}'.")

    def destroy(self):
        if self.__hd_plugins:
            self.__hd_plugins.destroy()
            self.__hd_plugins = None
        self.__renderers = {}
        self.__updated_fn = None

    def __del__(self):
        self.destroy()

    def __add_pxr_renderers(self, plugins):
        for renderer, desc in plugins.renderers:
            pluginID = renderer.typeName
            displayName = desc.get("displayName")
            # Special case Storm's displayName which is still 'GL'
            if pluginID == "HdStormRendererPlugin":
                displayName = "Pixar Storm"
            self.__add_renderer("pxr", self.PXR_RENDER_MODE_PATH, pluginID, displayName, desc.get("plugin"))

    def __add_renderer(self, engineName: str, renderModePath: str, pluginID: str, displayName=None, usdPlugin=None):
        if not displayName:
            displayName = pluginID
        engineRenderers = self.__renderers.get(engineName)
        if not engineRenderers:
            engineRenderers = HdEngineRenderer(engineName, renderModePath)
            self.__renderers[engineName] = engineRenderers
        elif any([True for r in engineRenderers.renderers if r.pluginID == pluginID]):
            return
        engineRenderers.renderers.append(HdRenderer(pluginID, displayName, usdPlugin))
        if self.__updated_fn:
            self.__updated_fn(self)

    @property
    def renderers(self):
        for k, v in self.__renderers.items():
            yield k, v
