import omni.ui as ui
from omni.kit.widget.settings import SettingType
import omni.kit.commands
from omni.rtx.window.settings.rtx_settings_stack import RTXSettingsStack
from omni.rtx.window.settings.settings_collection_frame import SettingsCollectionFrame
from omni.kit.widget.settings import SettingsWidgetBuilder
import carb


class GeometrySettingsFrame(SettingsCollectionFrame):
    """ Geometry """
    def _build_ui(self):
        tbnMode = ["Auto", "CPU", "GPU", "Force GPU"]
        scale_option = {"Default": -1, "Small - 1cm": 0.01, "Medium - 10 cm": 0.1, "Big - 100 cm": 1}
        with ui.CollapsableFrame("Misc Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                self._add_setting_combo("Normal & Tangent Space Generation Mode", "/rtx/hydra/TBNFrameMode", tbnMode,
                                        tooltip="\nMode selection for vertex Normals and Tangents generation. Options are:"
                                                "\n-AUTO: Selects the mode depending on available data and data update pattern."
                                                "\n-CPU: Uses mikktspace to generate tangent basis on the CPU."
                                                "\n-GPU: Allows normal and tangent basis update on the GPU to avoid the CPU overhead, such as for deforming meshes."
                                                "\n-Force GPU: Forces GPU mode.")
                self._add_setting(SettingType.BOOL, "Back Face Culling", "/rtx/hydra/faceCulling/enabled",
                                  tooltip="\nEnables back face culling for 'Single Sided' primitives."
                                          "\nThis applies to UsdGeom prims set to 'Single Sided' since the Double Sided flag is ignored.")
                self._add_setting(SettingType.BOOL, "USD ST Main Texcoord as Default UV Set", "/rtx/hydra/uvsets/stIsDefaultUvSet", tooltip="\nIf enabled, ST is considered to be the default UV set name.")
                self._add_setting(SettingType.BOOL, "Hide Geometry That Uses Opacity (debug)", "/rtx/debug/onlyOpaqueRayFlags", tooltip="\nAllows hiding all objects which have opacity enabled in their material.")
                self._add_setting(SettingType.BOOL, "Instance Selection", "/rtx/hydra/instancePickingEnabled", tooltip="\nEnables the picking of instances.")
                # if set to zero, override to scene unit, which means the scale factor would be 1
                self._add_setting_combo("Renderer-Internal Meters Per Unit", "/rtx/scene/renderMeterPerUnit", scale_option,
                                        tooltip="\nNumber of units per meter used by the renderer relative to the scene scale."
                                                "\nSome materials depend on scene scale, such as subsurface scattering and volumetric shading.")
                self._add_setting(SettingType.FLOAT, "Ray Offset (0 = auto)", "/rtx/raytracing/rayOffset", 0.0, 5.0, 0.001, tooltip="\nOffset used to prevent ray-triangle self intersections." )

                self._add_setting(SettingType.FLOAT, "Points Default Width", "/persistent/rtx/hydra/points/defaultWidth", tooltip="\nUses this value if width is not specified in UsdGeomPoints.")

        with ui.CollapsableFrame("Wireframe Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                self._add_setting(SettingType.BOOL, "Per-primitive Wireframe uses World Space Thickness", "/rtx/wireframe/wireframeThicknessWorldSpace", tooltip="\nInterprets the per-primitive wireframe thickness value in world space instead of screen space.")
                self._add_setting(SettingType.BOOL, "Global Wireframe uses World Space Thickness", "/rtx/wireframe/globalWireframeThicknessWorldSpace", tooltip="\nInterprets the global wireframe thickness value in world space instead of screen space.")
                self._add_setting(SettingType.FLOAT, "Thickness", "/rtx/wireframe/wireframeThickness", 0.1, 100, 0.1, tooltip="\nWireframe thickness in wireframe mode.")

        def clear_refinement_overrides():
            omni.kit.commands.execute("ClearRefinementOverrides")

        with ui.CollapsableFrame("Subdivision Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                self._add_setting(SettingType.INT, "Global Refinement Level", "/rtx/hydra/subdivision/refinementLevel", 0, 8, hard_range=True,
                                  tooltip="\nThe refinement level for all primitives with Subdivision Schema not set to None."
                                          "\nEach increment increases the mesh triangle count by a factor of 4.")
                self._add_setting(SettingType.BOOL, "Feature-Adaptive Refinement", "/rtx/hydra/subdivision/adaptiveRefinement",
                                  tooltip="\nIncreases or reduces the refinement level based on geometric features."
                                          "\nThis reduces the number of triangles used in flat areas for example.")
                ui.Button("Clear Refinement Override in All Prims", clicked_fn=clear_refinement_overrides, tooltip="\nClears the Refinement Override set in all Prims.")

        def clear_splits_overrides():
            omni.kit.commands.execute("ClearCurvesSplitsOverridesCommand")

        with ui.CollapsableFrame("Curves Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                self._add_setting(SettingType.INT, "Global Number of BVH Splits", "/rtx/hydra/curves/splits", 1, 8, hard_range=True,
                                  tooltip="\nHigher number of splits results in faster rendering but longer BVH build time."
                                          "\nThe speed up depends on the geometry: long and thin curve segments tend to benefit from more splits."
                                          "\nMemory used by the BVH grows linearly with the number of splits.")
                ui.Button("Clear Number of BVH Splits Overrides in All Prims", clicked_fn=clear_splits_overrides, tooltip="\nClears the Number of BVH Splits Refinement Override set in all Prims.")


class MaterialsSettingsFrame(SettingsCollectionFrame):
    """ Materials """
    def _on_change(self, *_):
        self._rebuild()

    def _build_ui(self):

        self._change_cb1 = omni.kit.app.SettingChangeSubscription("/app/renderer/skipMaterialLoading", self._on_change)
        self._change_cb2 = omni.kit.app.SettingChangeSubscription("/rtx-transient/resourcemanager/enableTextureStreaming", self._on_change)

        # The setting is maxMipCount, to make it more user friendly we expose it in the API as max resolution
        texture_mip_sizes = {
            "64": 7,
            "128": 8,
            "256": 9,
            "512": 10,
            "1024": 11,
            "2048": 12,
            "4096": 13,
            "8192": 14,
            "16384": 15,
            "32768": 16,
        }

        self._add_setting(SettingType.BOOL, "Disable Material Loading", "/app/renderer/skipMaterialLoading", tooltip="\nScenes will be loaded without materials. This can lower scene loading time.")
        if not self._settings.get("/app/renderer/skipMaterialLoading"):
            with ui.CollapsableFrame("Texture Compression Settings", height=0):
                with ui.VStack(height=0, spacing=5):
                    self._add_setting_combo("Max Resolution", "/rtx-transient/resourcemanager/maxMipCount", texture_mip_sizes, tooltip="\nTextures larger than this will be downsampled at this resolution.")
                    self._add_setting(SettingType.INT, "Compression Size Threshold", "/rtx-transient/resourcemanager/compressionMipSizeThreshold", 0, 8192, tooltip="\nTextures smaller than this size won't be compressed. 0 disables compression.")
                    self._add_setting(SettingType.BOOL, "Normal Map Mip-Map Generation (toggling requires scene reload)", "/rtx-transient/resourcemanager/genMipsForNormalMaps", tooltip="\nEnables mip-map generation for normal maps to reduce memory usage at the expense of quality.")
                    # if self._settings.get("/rtx-transient/resourcemanager/genMipsForNormalMaps"):
                        # self._add_setting(SettingType.BOOL, "Normal Map Roughness generation (toggling requires scene reload)", "/rtx-transient/resourcemanager/createNormalRoughness", tooltip="\nEnables roughness generation for normal maps for improved specular reflection with mip mapping enabled.")
            with ui.CollapsableFrame("Texture Streaming Settings", height=0):
                with ui.VStack(height=0, spacing=5):
                    self._add_setting(SettingType.BOOL, "Enable Texture Streaming (toggling requires scene reload)", "/rtx-transient/resourcemanager/enableTextureStreaming", tooltip="\nEnables texture streaming.")
                    if self._settings.get("/rtx-transient/resourcemanager/enableTextureStreaming"):
                        self._add_setting(SettingType.FLOAT, "   Memory Budget (% of GPU memory)", "/rtx-transient/resourcemanager/texturestreaming/memoryBudget", 0, 1, tooltip="\nLimits the GPU memory budget used for texture streaming.")
                        self._add_setting(SettingType.INT, "   Budget Per Request (in MB)", "/rtx-transient/resourcemanager/texturestreaming/streamingBudgetMB", 0, 10000,
                                          tooltip="\nMaximum budget per streaming request. 0 = unlimited but could lead to stalling during streaming."
                                                  "\nHigh or unlimited budget could lead to stalling during streaming.")
            with ui.CollapsableFrame("MDL Settings", height=0):
                with ui.VStack(height=0, spacing=5):
                    self._add_setting(SettingType.FLOAT, "Animation Time Override", "/rtx/animationTime", tooltip="\nOverrides the time value provided to MDL materials.")
                    self._add_setting(SettingType.BOOL, "Animation Time Use Wallclock", "/rtx/animationTimeUseWallclock", tooltip="\nUse actual elapsed time for animation instead of simulated time.")

    def destroy(self):
        self._change_cb1 = None
        self._change_cb2 = None
        super().destroy()


class LightingSettingsFrame(SettingsCollectionFrame):
    """ Lighting """
    def _build_ui(self):
        with ui.CollapsableFrame("Light Visibility Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                # Common show light setting for all render modes - this makes the /pathracing/showLights and /directLighting/showLights deprecated and should be used instead
                # see LightTypes.hlslh for enum
                show_lights_settings = {
                    "Per-Light Enable": 0,
                    "Force Enable": 1,
                    "Force Disable": 2
                }
                self._add_setting_combo("Show Area Lights In Primary Rays", "/rtx/raytracing/showLights", show_lights_settings, tooltip="\nDefines if area lights are visible or invisible in primary rays.")
                self._add_setting(SettingType.FLOAT, "Invisible Lights Roughness Threshold", "/rtx/raytracing/invisLightRoughnessThreshold", 0, 1, 0.001,
                                  tooltip="\nDefines the roughness threshold below which lights invisible in primary rays"
                                          "\nare also invisible in reflections and refractions.")
                self._add_setting(SettingType.BOOL, "Use First Distant Light & First Dome Light Only", "/rtx/scenedb/skipMostLights", tooltip="\nDisable all lights except the first distant light and first dome light.")
                self._add_setting(SettingType.FLOAT, "Shadow Bias", "/rtx/raytracing/shadowBias", 0.0, 5.0, 0.001, tooltip="\nOffset applied for shadow ray origin along the surface normal. Reduces self-shadowing artifacts.")

        with ui.CollapsableFrame("Dome Light Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                dome_lighting_sampling_type = {
                    "Image-Based Lighting": 0,
                    "Approximated Image-Based Lighting": 4,
                    "Environment Mapped Image-Based Lighting": 3
                }
                self._add_setting_combo("Lighting Mode", "/rtx/domeLight/upperLowerStrategy", dome_lighting_sampling_type,
                                        tooltip="\n-Image-Based Lighting: Most accurate even for high-frequency Dome Light textures. Can introduce sampling artefacts in real-time mode."
                                                "\n-Approximated Image-Based Lighting: Fast and artefacts-free sampling in real-time mode but only works well with a low-frequency texture,"
                                                "\nfor example a sky with no sun disc where the sun is instead a separate Distant Light. Requires enabling Direct Lighting denoiser."
                                                "\n-Limited Image-Based Lighting: Only sampled for reflection and refraction. Fastest, but least accurate. Good for cases where the Dome Light"
                                                "\ncontributes less than other light sources.")
                dome_texture_resolution_items = {
                    "16": 16,
                    "32": 32,
                    "64": 64,
                    "128": 128,
                    "256": 256,
                    "512": 512,
                    "1024": 1024,
                    "2048": 2048,
                    "4096": 4096,
                    "8192": 8192,
                }
                self._add_setting_combo("Baking Resolution", "/rtx/domeLight/baking/resolution", dome_texture_resolution_items, tooltip="\nThe baking resolution of the Dome Light texture when an MDL material is used as its image source.")


class GlobalVolumetricEffectsSettingsFrame(SettingsCollectionFrame):
    """ Global Volumetric Effects Common Settings RT & PT """
    def _frame_setting_path(self):
        return "/rtx/raytracing/globalVolumetricEffects/enabled"

    def _build_ui(self):
        self._add_setting(SettingType.FLOAT, "Fog Height", "/rtx/raytracing/inscattering/atmosphereHeight", -2000, 100000, 10, tooltip="\nHeight in world units (centimeters) at which the medium ends. Useful for atmospheres with distant lights or dome lights.")
        self._add_setting(SettingType.FLOAT, "Fog Height Fall Off", "/rtx/pathtracing/ptvol/fogHeightFallOff", 10, 2000, 1, tooltip="\nExponential decay of the fog above the Fog Height.")
        self._add_setting(SettingType.FLOAT, "Maximum inscattering Distance", "/rtx/raytracing/inscattering/maxDistance", 10, 1000000,
                          tooltip="\nMaximum depth in world units (centimeters) the voxel grid is allocated to."
                                  "\nIf set to 10,000 with 10 depth slices, each slice will span 1,000 units (assuming a slice distribution exponent of 1)."
                                  "\nIdeally this should be kept as low as possible without causing artifacts to make the most of the fixed number of depth slices.")
        self._add_setting(SettingType.FLOAT, "Density Multiplier", "/rtx/raytracing/inscattering/densityMult", 0, 2, 0.001, tooltip="\nScales the fog density.")
        self._add_setting(SettingType.FLOAT, "Transmittance Measurment Distance", "/rtx/raytracing/inscattering/transmittanceMeasurementDistance", 0.0001, 1000000, 10, tooltip="\nControls how far light can travel through fog. Lower values yield thicker fog.")
        self._add_setting(SettingType.COLOR3, "Transmittance Color", "/rtx/raytracing/inscattering/transmittanceColor",
                          tooltip="\nAssuming a white light, it represents its tint after traveling a number of units through"
                                  "\nthe volume as specified in Transmittance Measurement Distance.")
        self._add_setting(SettingType.COLOR3, "Single Scattering Albedo", "/rtx/raytracing/inscattering/singleScatteringAlbedo", tooltip="\nThe ratio of scattered-light to attenuated-light for an interaction with the volume. Values closer to 1 indicate high scattering.")
        self._add_setting(SettingType.FLOAT, "Anisotropy Factor (g)", "/rtx/raytracing/inscattering/anisotropyFactor", -0.999, 0.999, 0.01,
                          tooltip="\nAnisotropy of the volumetric phase function, or the degree of light scattering asymmetry."
                                  "\n-1 is back-scattered, 0 is isotropic, 1 is forward-scattered.")
        with ui.CollapsableFrame("Density Noise Settings", height=0):
            with ui.VStack(height=0, spacing=5):
                self._add_setting(SettingType.BOOL, "Apply Density Noise", "/rtx/raytracing/inscattering/useDetailNoise", tooltip="\nEnables modulating the density with a noise. Enabling this option can reduce performance.")
                self._change_cb1 = omni.kit.app.SettingChangeSubscription("/rtx/raytracing/inscattering/useDetailNoise", self._on_change)
                if self._settings.get("/rtx/raytracing/inscattering/useDetailNoise"):
                    self._add_setting(SettingType.FLOAT, "   World Scale", "/rtx/raytracing/inscattering/detailNoiseScale", 0.0, 1, 0.00001, tooltip="\nA scale multiplier for the noise. Smaller values produce more sparse noise.")
                    self._add_setting(SettingType.FLOAT, "   Animation Speed X", "/rtx/raytracing/inscattering/noiseAnimationSpeedX", -1.0, 1.0, 0.01, tooltip="\nThe X vector for the noise shift when animated.")
                    self._add_setting(SettingType.FLOAT, "   Animation Speed Y", "/rtx/raytracing/inscattering/noiseAnimationSpeedY", -1.0, 1.0, 0.01, tooltip="\nThe Y vector for the noise shift when animated.")
                    self._add_setting(SettingType.FLOAT, "   Animation Speed Z", "/rtx/raytracing/inscattering/noiseAnimationSpeedZ", -1.0, 1.0, 0.01, tooltip="\nThe Z vector for the noise shift when animated.")
                    self._add_setting(SettingType.FLOAT, "   Scale Min", "/rtx/raytracing/inscattering/noiseScaleRangeMin", -1.0, 5.0, 0.01,
                                      tooltip="\nA range to map the noise values of each noise octave."
                                              "\nTypically these should be 0 and 1."
                                              "\nTo make sparse points in the noise less sparse a higher Min can be used, or a lower Max for dense points to be less dense.")
                    self._add_setting(SettingType.FLOAT, "   Scale Max", "/rtx/raytracing/inscattering/noiseScaleRangeMax", -1.0, 5.0, 0.01,
                                      tooltip="\nA range to map the noise values of each noise octave."
                                              "\nTypically these should be 0 and 1. To make sparse points in the noise less sparse"
                                              "\na higher Min can be used, or a lower Max for dense points to be less dense.")
                    self._add_setting(SettingType.INT, "   Octave Count", "/rtx/raytracing/inscattering/noiseNumOctaves", 1, 8, tooltip="\nA higher octave count results in greater noise detail, at the cost of performance.")

    def destroy(self):
        self._change_cb1 = None
        super().destroy()


class SimpleFogSettingsFrame(SettingsCollectionFrame):
    """ Simple Fog """
    def _frame_setting_path(self):
        return "/rtx/fog/enabled"

    def _build_ui(self):
        self._add_setting(SettingType.COLOR3, "Color", "/rtx/fog/fogColor", tooltip="\nThe color or tint of the fog volume.")
        self._add_setting(SettingType.FLOAT, "Intensity", "/rtx/fog/fogColorIntensity", 1, 1000000, 1, tooltip="\nThe intensity of the fog effect.")
        self._add_setting(SettingType.BOOL, "Height-based Fog - Use +Z Axis", "/rtx/fog/fogZup/enabled", tooltip="\nUse positive Z axis for height-based fog. Otherwise use the positive Y axis.")
        self._add_setting(SettingType.FLOAT, "Height-based Fog - Plane Height", "/rtx/fog/fogStartHeight", -1000000, 1000000, 0.01, tooltip="\nThe starting height (in meters) for height-based fog.")
        self._add_setting(SettingType.FLOAT, "Height Density", "/rtx/fog/fogHeightDensity", 0, 1, 0.001, tooltip="\nDensity of the height-based fog. Higher values result in thicker fog.")
        self._add_setting(SettingType.FLOAT, "Height Falloff", "/rtx/fog/fogHeightFalloff", 0, 1000, 0.002, tooltip="\nRate at which the height-based fog falls off.")
        self._add_setting(SettingType.FLOAT, "Start Distance to Camera", "/rtx/fog/fogStartDist", 0, 1000000, 0.1, tooltip="\nDistance from the camera at which the fog begins.")
        self._add_setting(SettingType.FLOAT, "End Distance to Camera", "/rtx/fog/fogEndDist", 0, 1000000, 0.1, tooltip="\nDistance from the camera at which the fog achieves maximum density.")
        self._add_setting(SettingType.FLOAT, "Distance Density", "/rtx/fog/fogDistanceDensity", 0, 1, 0.001, tooltip="\nThe fog density at the End Distance.")

    def destroy(self):
        super().destroy()


class FlowSettingsFrame(SettingsCollectionFrame):
    """ Flow """
    def _frame_setting_path(self):
        return "/rtx/flow/enabled"

    def _build_ui(self):
        self._add_setting(SettingType.BOOL, "Flow in Real-Time Ray Traced Shadows", "/rtx/flow/rayTracedShadowsEnabled")
        self._add_setting(SettingType.BOOL, "Flow in Real-Time Ray Traced Reflections", "/rtx/flow/rayTracedReflectionsEnabled")
        self._add_setting(SettingType.BOOL, "Flow in Real-Time Ray Traced Translucency", "/rtx/flow/rayTracedTranslucencyEnabled")
        self._add_setting(SettingType.BOOL, "Flow in Path-Traced Mode", "/rtx/flow/pathTracingEnabled")
        self._add_setting(SettingType.BOOL, "Flow in Path-Traced Mode Shadows", "/rtx/flow/pathTracingShadowsEnabled")
        self._add_setting(SettingType.BOOL, "Composite with Flow Library Renderer", "/rtx/flow/compositeEnabled")
        self._add_setting(SettingType.BOOL, "Use Flow Library Self Shadow", "/rtx/flow/useFlowLibrarySelfShadow")
        self._add_setting(SettingType.INT, "Max Blocks", "/rtx/flow/maxBlocks")

    def destroy(self):
        super().destroy()


class IndexCompositeSettingsFrame(SettingsCollectionFrame):
    """ NVIDIA IndeX Compositing """
    def _frame_setting_path(self):
        return "/rtx/index/compositeEnabled"

    def _build_ui(self):
        ui.Label("You can activate IndeX composite rendering for individual Volume or Points prims "
                 "by enabling their 'Use IndeX compositing' property.", word_wrap=True)
        ui.Separator()

        depth_compositing_settings = {
            "Disable": 0,
            "Before Anti-Aliasing": 1,
            "After Anti-Aliasing": 2,
            "After Anti-Aliasing (Stable Depth)": 3
        }

        self._add_setting_combo(
            "Depth Compositing",
            "/rtx/index/compositeDepthMode",
            depth_compositing_settings,
            tooltip="Depth-correct compositing between renderers")

        self._add_setting(
            SettingType.FLOAT,
            "Color Scaling",
            "/rtx/index/colorScale",
            0.0,
            100.0,
            tooltip="Scale factor for color output",
        )

        self._add_setting(
            SettingType.BOOL,
            "sRGB Conversion",
            "/rtx/index/srgbConversion",
            tooltip="Apply color space conversion to IndeX rendering",
        )

        self._add_setting(
            SettingType.INT,
            "Resolution Scaling",
            "/rtx/index/resolutionScale",
            1,
            100,
            tooltip="Reduces the IndeX rendering resolution (in percent relative to the viewport resolution)",
        )

        self._add_setting(
            SettingType.INT,
            "Rendering Samples",
            "/rtx/index/renderingSamples",
            1,
            32,
            tooltip="Number of samples per pixel used during rendering",
        )

        self._add_setting(
            SettingType.FLOAT,
            "Default Point Width",
            "/rtx/index/defaultPointWidth",
            tooltip="Default point width for new point clouds loaded from USD. If set to 0, a simple heuristic will be used.",
        )

    def destroy(self):
        super().destroy()


class DebugViewSettingsFrame(SettingsCollectionFrame):
    """ Debug View """
    def _on_debug_view_change(self, *_):
        self._rebuild()


    def _build_ui(self):
        debug_view_items = {
            "Off": "",
            "RT Developer Debug Texture": "developerDebug",
            "3D Motion Vectors [WARNING: Flashing Colors]": "targetMotion",
            "3D Motion Vector Arrows [WARNING: Flashing Colors]": "targetMotionArrows",
            "3D Final Motion Vector Arrows [WARNING: Flashing Colors]": "finalMotion",
            "Barycentrics": "barycentrics",
            "Beauty After Tonemap": "beautyPostTonemap",
            "Beauty Before Tonemap": "beautyPreTonemap",
            "Depth": "depth",
            "Instance ID": "instanceId",
            "Interpolated Normal": "normal",
            "Heat Map: Any Hit": "anyHitCountHeatMap",
            "Heat Map: Intersection": "intersectionCountHeatMap",
            "Heat Map: Timing": "timingHeatMap",
            "SDG: Cross Correspondence": "sdgCrossCorrespondence",
            "SDG: Motion": "sdgMotion",
            "Tangent U": "tangentu",
            "Tangent V": "tangentv",
            "Texture Coordinates 0": "texcoord0",
            "Texture Coordinates 1": "texcoord1",
            "Triangle Normal": "triangleNormal",
            "Triangle Normal (OctEnc)": "triangleNormalOctEnc",  # ReLAX Only
            "Wireframe": "wire",
            "RT Ambient Occlusion": "ao",
            "RT Caustics": "caustics",
            "RT Denoised Dome Light": "denoisedDomeLightingTex",
            "RT Denoised Sampled Lighting": "rtDenoisedSampledLighting",
            "RT Denoised Sampled Lighting Diffuse": "rtDenoiseSampledLightingDiffuse",  # ReLAX Only
            "RT Denoised Sampled Lighting Specular": "rtDenoiseSampledLightingSpecular",  # ReLAX Only
            "RT Diffuse GI": "indirectDiffuse",
            "RT Diffuse GI (Not Accumulated)": "indirectDiffuseNonAccum",
            "RT Diffuse Reflectance": "diffuseReflectance",
            "RT Material Normal": "materialGeometryNormal",
            "RT Material Normal (OctEnc)": "materialGeometryNormalOctEnc",  # ReLAX Only
            "RT Matte Object Compositing Alpha": "matteObjectAlpha",
            "RT Matte Object Mask": "matteObjectMask",
            "RT Matte Object View Before Postprocessing": "matteBeforePostprocessing",
            "RT Noisy Dome Light": "noisyDomeLightingTex",
            "RT Noisy Sampled Lighting": "rtNoisySampledLighting",
            "RT Noisy Sampled Lighting (Not Accumulated)": "rtNoisySampledLightingNonAccum",
            "RT Noisy Sampled Lighting Diffuse": "rtNoisySampledLightingDiffuse",  # ReLAX Only
            "RT Noisy Sampled Lighting Diffuse (Not Accumulated)": "sampledLightingDiffuseNonAccum",
            "RT Noisy Sampled Lighting Specular": "rtNoisySampledLightingSpecular",  # ReLAX Only
            "RT Noisy Sampled Lighting Specular (Not Accumulated)": "sampledLightingSpecularNonAccum",
            "RT Radiance": "radiance",
            "RT Subsurface Radiance": "subsurface",
            "RT Subsurface Transmission Radiance": "subsurfaceTransmission",
            "RT Reflections": "reflections",
            "RT Reflections (Not Accumulated)": "reflectionsNonAccum",
            "RT Reflections 3D Motion Vectors [WARNING: Flashing Colors]": "reflectionsMotion",
            "RT Roughness": "roughness",
            "RT Shadow (last light)": "shadow",
            "RT Specular Reflectance": "reflectance",
            "RT Translucency": "translucency",
            "RT World Position": "worldPosition",
            "PT Adaptive Sampling Error [WARNING: Flashing Colors]": "PTAdaptiveSamplingError",
            "PT Denoised Result": "pathTracerDenoised",
            "PT Noisy Result": "pathTracerNoisy",
            "PT AOV Background": "PTAOVBackground",
            "PT AOV Diffuse Filter": "PTAOVDiffuseFilter",
            "PT AOV Direct Illumation": "PTAOVDI",
            "PT AOV Global Illumination": "PTAOVGI",
            "PT AOV Reflections": "PTAOVReflections",
            "PT AOV Reflection Filter": "PTAOVReflectionFilter",
            "PT AOV Refractions": "PTAOVRefractions",
            "PT AOV Refraction Filter": "PTAOVRefractionFilter",
            "PT AOV Self-Illumination": "PTAOVSelfIllum",
            "PT AOV Volumes": "PTAOVVolumes",
            "PT AOV World Normal": "PTAOVWorldNormals",
            "PT AOV World Position": "PTAOVWorldPos",
            "PT AOV Z-Depth": "PTAOVZDepth",
            "PT AOV Multimatte0": "PTAOVMultimatte0",
            "PT AOV Multimatte1": "PTAOVMultimatte1",
            "PT AOV Multimatte2": "PTAOVMultimatte2",
            "PT AOV Multimatte3": "PTAOVMultimatte3",
            "PT AOV Multimatte4": "PTAOVMultimatte4",
            "PT AOV Multimatte5": "PTAOVMultimatte5",
            "PT AOV Multimatte6": "PTAOVMultimatte6",
            "PT AOV Multimatte7": "PTAOVMultimatte7",
        }
        self._add_setting_searchable_combo("Render Target", "/rtx/debugView/target", debug_view_items, "Off", "\nA list of all render passes which can be visualized.")

        debug_view_target = self._settings.get("/rtx/debugView/target")

        # Trigger the combo update to reflect the current selection when the ui is rebuilt
        self._settings.set("/rtx/debugView/target", debug_view_target);

        # Subscribe this frame to the target setting so we can show the heat map controls if necessary.
        # This will remove the subscription from the searchable combo, but we will have a chance to update it
        # because the new callback does a rebuild.
        self._change_cb1 = omni.kit.app.SettingChangeSubscription("/rtx/debugView/target", self._on_debug_view_change)

        is_timing_heat_map = debug_view_target == "timingHeatMap"
        is_any_hit_count_heat_map = debug_view_target == "anyHitCountHeatMap"
        is_intersection_count_heat_map = debug_view_target == "intersectionCountHeatMap"

        if is_timing_heat_map or is_any_hit_count_heat_map or is_intersection_count_heat_map:
            #  'heat_map_view_items' MUST perfectly match with 'HeatMapSelectablePass' in 'RtxRendererContext.h'
            heat_map_view_items = {
                "GBuffer RT": 1,
                "Path Tracing": 2,
                "Shadow RT": 3,
                "Deferred LTC Lighting RT": 4,
                "Deferred Sampled Lighting RT": 5,
                "Reflections LTC RT ": 6,
                "Reflections Sampled RT": 7,
                "Back Lighting": 8,
                "Translucency RT": 9,
                "SSS RT": 10,
                "Transmission RT": 11,
                "Indirect Diffuse RT (Apply Cache)": 12,
                "Ray Traced Ambient Occlusion": 13
            }

            # 'heat_map_color_palette_items' MUST perfectly match with condition in 'temperature' function in 'DebugView.cs.hlsl'
            heat_map_color_palette_items = {
                "Rainbow": 0,
                "Viridis": 1,
                "Turbo": 2,
                "Plasma": 3
            }

            self._add_setting_combo("   Pass To Visualize ", "/rtx/debugView/heatMapPass", heat_map_view_items)

            if is_timing_heat_map:
                self._add_setting(SettingType.FLOAT, "   Maximum Time (µs)", "/rtx/debugView/heatMapMaxTime", 0, 100000.0, 1.0)
            elif is_any_hit_count_heat_map:
                self._add_setting(SettingType.INT, "   Maximum Any Hit", "/rtx/debugView/heatMapMaxAnyHitCount", 1, 100)
            elif is_intersection_count_heat_map:
                self._add_setting(SettingType.INT, "   Maximum Intersection", "/rtx/debugView/heatMapMaxIntersectionCount", 1, 100)

            self._add_setting_combo("   Color Palette", "/rtx/debugView/heatMapColorPalette", heat_map_color_palette_items)
            self._add_setting(SettingType.BOOL, "   Show Color Bar", "/rtx/debugView/heatMapOverlayHeatMapScale")
        else:
            self._add_setting(SettingType.FLOAT, "   Output Value Scaling", "/rtx/debugView/scaling", -1000000, 1000000, 1.0, tooltip="\nScales the output value by this factor. Useful to accentuate differences.")

class MaterialFlattenerSettingsFrame(SettingsCollectionFrame):
    """ Material Flattening """
    def _build_ui(self):
        import carb
        if not carb.settings.get_settings().get("/rtx/materialflattener/enabled"):
            ui.Label("Material flattener disabled, rerun with --/rtx/materialflattener/enabled=true")
        else:
            planarProjectionPlaneAxes = ["auto", "xy", "xz", "yz"]
            self._add_setting_combo("Planar Projection Plane Axes", "/rtx/materialflattener/planarProjectionPlaneAxes", planarProjectionPlaneAxes)

            self._add_setting(SettingType.BOOL, "Show Decal Sources", "/rtx/materialflattener/showSources")
            self._add_setting(SettingType.BOOL, "Live Baking", "/rtx/materialflattener/liveBaking")
            self._add_setting(SettingType.BOOL, "Rebaking", "/rtx/materialflattener/rebaking")
            self._add_setting(SettingType.BOOL, "Clear before baking", "/rtx/materialflattener/clearMaterialInputsBeforeBaking")

            coverageSamples = ["Disabled", "4", "16", "64", "256"]
            self._add_setting_combo("Decal Antialiasing Samples", "/rtx/materialflattener/decalCoverageSamples", coverageSamples)

            self._add_setting(SettingType.BOOL, "Disable tile budget", "/rtx/materialflattener/disableTileBudget")
            self._add_setting(SettingType.INT, "Number of tiles per frame", "/rtx/materialflattener/maxNumTilesToBakePerFrame")
            self._add_setting(SettingType.INT, "Number of texels per frame", "/rtx/materialflattener/maxNumTexelsToBakePerFrame")
            self._add_setting(SettingType.INT, "Number of material components per bake pass", "/rtx/materialflattener/numMaterialComponentsPerBakePass", 1, 12)
            self._add_setting(SettingType.INT, "VTex Group to bake (-1 ~ all)", "/rtx/materialflattener/vtexGroupToBake", -1, 1023)
            self._add_setting(SettingType.BOOL, "Radius based tile residency", "/rtx/materialflattener/radiusBasedResidency")
            self._add_setting(SettingType.FLOAT, "Radius based tile residency - radius", "/rtx/materialflattener/radiusBasedResidencyRadius", 0, 10000, 0.1)

            self._add_setting(SettingType.FLOAT, "Lod: derivative scale", "/rtx/virtualtexture/derivativeScale", 0.01, 100, 0.01)
            self._add_setting(SettingType.FLOAT, "Decal distance tolerance", "/rtx/materialflattener/decalDistanceTolerance", 0, 1000, 0.01)
            self._add_setting(SettingType.FLOAT, "Sampling LOD Bias", "/rtx/textures/lodBias", -15, 15, 0.01)
            self._add_setting(SettingType.INT, "VTex Group ID color option (0 = disabled)", "/rtx/virtualtexture/colorCodeByVtexGroupIdOption", 0, 4)
            self._add_setting(SettingType.INT, "Baking LOD Bias (on top of Sampling LOD bias)", "/rtx/materialflattener/bakingLodBias", -15, 12)


class CommonSettingStack(RTXSettingsStack):

    def __init__(self) -> None:
        self._stack = ui.VStack(spacing=7, identifier=__class__.__name__)
        with self._stack:
            GeometrySettingsFrame("Geometry", parent=self)
            MaterialsSettingsFrame("Materials", parent=self)
            LightingSettingsFrame("Lighting", parent=self)
            SimpleFogSettingsFrame("Simple Fog", parent=self)
            GlobalVolumetricEffectsSettingsFrame("Global Volumetric Effects", parent=self)
            FlowSettingsFrame("Flow", parent=self)

            # Only show IndeX settings if the extension is loaded
            if carb.settings.get_settings().get("/nvindex/compositeRenderingMode"):
                IndexCompositeSettingsFrame("NVIDIA IndeX Compositing", parent=self)

            # MaterialFlattenerSettingsFrame("Material Flattener", parent=self)
            DebugViewSettingsFrame("Debug View", parent=self)
