// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using Object = UnityEngine.Object;



public class ShadowStandardShaderGUI : ShadowShaderGUI {
    protected enum AlbedoAlphaMode {
        Transparency,
        Metallic,
        Smoothness
    }

    protected static class Styles {
        public static string primaryMapsTitle = "Main Maps";
        public static string renderingOptionsTitle = "Rendering Options";
        public static string advancedOptionsTitle = "Advanced Options";
        public static string fluentOptionsTitle = "Fluent Options";
        public static string instancedColorName = "_InstancedColor";
        public static string instancedColorFeatureName = "_INSTANCED_COLOR";
        public static string stencilComparisonName = "_StencilComparison";
        public static string stencilOperationName = "_StencilOperation";
        public static string disableAlbedoMapName = "_DISABLE_ALBEDO_MAP";
        public static string albedoMapAlphaMetallicName = "_METALLIC_TEXTURE_ALBEDO_CHANNEL_A";
        public static string albedoMapAlphaSmoothnessName = "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A";
        public static string propertiesComponentHelp = "Use the {0} component(s) to control {1} properties.";
        public static readonly string[] albedoAlphaModeNames = Enum.GetNames(typeof(AlbedoAlphaMode));
        public static GUIContent instancedColor = new GUIContent("Instanced Color", "Enable a Unique Color Per Instance");
        public static GUIContent albedo = new GUIContent("Albedo", "Albedo (RGB) and Transparency (Alpha)");
        public static GUIContent albedoAssignedAtRuntime = new GUIContent("Assigned at Runtime", "As an optimization albedo operations are disabled when no albedo texture is specified. If a albedo texture will be specified at runtime enable this option.");
        public static GUIContent alphaCutoff = new GUIContent("Alpha Cutoff", "Threshold for Alpha Cutoff");
        public static GUIContent metallic = new GUIContent("Metallic", "Metallic Value");
        public static GUIContent smoothness = new GUIContent("Smoothness", "Smoothness Value");
        public static GUIContent enableChannelMap = new GUIContent("Channel Map", "Enable Channel Map, a Channel Packing Texture That Follows Unity's Standard Channel Setup");
        public static GUIContent channelMap = new GUIContent("Channel Map", "Metallic (Red), Occlusion (Green), Emission (Blue), Smoothness (Alpha)");
        public static GUIContent enableNormalMap = new GUIContent("Normal Map", "Enable Normal Map");
        public static GUIContent normalMap = new GUIContent("Normal Map");
        public static GUIContent normalMapScale = new GUIContent("Scale", "Scales the Normal Map Normal");
        public static GUIContent enableEmission = new GUIContent("Emission", "Enable Emission");
        public static GUIContent emissiveColor = new GUIContent("Color");
        public static GUIContent enableTriplanarMapping = new GUIContent("Triplanar Mapping", "Enable Triplanar Mapping, a technique which programmatically generates UV coordinates");
        public static GUIContent enableLocalSpaceTriplanarMapping = new GUIContent("Local Space", "If True Triplanar Mapping is Calculated in Local Space");
        public static GUIContent triplanarMappingBlendSharpness = new GUIContent("Blend Sharpness", "The Power of the Blend with the Normal");
        public static GUIContent directionalLight = new GUIContent("Directional Light", "Affected by One Unity Directional Light");
        public static GUIContent specularHighlights = new GUIContent("Specular Highlights", "Calculate Specular Highlights");
        public static GUIContent sphericalHarmonics = new GUIContent("Spherical Harmonics", "Read From Spherical Harmonics Data for Ambient Light");
        public static GUIContent reflections = new GUIContent("Reflections", "Calculate Glossy Reflections");
        public static GUIContent refraction = new GUIContent("Refraction", "Calculate Refraction");
        public static GUIContent refractiveIndex = new GUIContent("Refractive Index", "Ratio of Indices of Refraction at the Surface Interface");
        public static GUIContent rimLight = new GUIContent("Rim Light", "Enable Rim (Fresnel) Lighting");
        public static GUIContent rimColor = new GUIContent("Color", "Rim Highlight Color");
        public static GUIContent rimPower = new GUIContent("Power", "Rim Highlight Saturation");
        public static GUIContent vertexColors = new GUIContent("Vertex Colors", "Enable Vertex Color Tinting");
        public static GUIContent vertexExtrusion = new GUIContent("Vertex Extrusion", "Enable Vertex Extrusion Along the Vertex Normal");
        public static GUIContent vertexExtrusionValue = new GUIContent("Vertex Extrusion Value", "How Far to Extrude the Vertex Along the Vertex Normal");
        public static GUIContent blendedClippingWidth = new GUIContent("Blended Clipping Width", "The Width of the Clipping Primitive Clip Fade Region on Non-Cutout Materials");
        public static GUIContent clippingBorder = new GUIContent("Clipping Border", "Enable a Border Along the Clipping Primitive's Edge");
        public static GUIContent clippingBorderWidth = new GUIContent("Width", "Width of the Clipping Border");
        public static GUIContent clippingBorderColor = new GUIContent("Color", "Interpolated Color of the Clipping Border");
        public static GUIContent nearPlaneFade = new GUIContent("Near Fade", "Objects Disappear (Turn to Black/Transparent) as the Camera (or Hover/Proximity Light) Nears Them");
        public static GUIContent nearLightFade = new GUIContent("Use Light", "A Hover or Proximity Light (Rather Than the Camera) Determines Near Fade Distance");
        public static GUIContent fadeBeginDistance = new GUIContent("Fade Begin", "Distance From Camera (or Hover/Proximity Light) to Begin Fade In");
        public static GUIContent fadeCompleteDistance = new GUIContent("Fade Complete", "Distance From Camera (or Hover/Proximity Light) When Fade is Fully In");
        public static GUIContent fadeMinValue = new GUIContent("Fade Min Value", "Clamps the Fade Amount to a Minimum Value");
        public static GUIContent hoverLight = new GUIContent("Hover Light", "Enable utilization of Hover Light(s)");
        public static GUIContent enableHoverColorOverride = new GUIContent("Override Color", "Override Global Hover Light Color for this Material");
        public static GUIContent hoverColorOverride = new GUIContent("Color", "Override Hover Light Color");
        public static GUIContent proximityLight = new GUIContent("Proximity Light", "Enable utilization of Proximity Light(s)");
        public static GUIContent enableProximityLightColorOverride = new GUIContent("Override Color", "Override Global Proximity Light Color for this Material");
        public static GUIContent proximityLightCenterColorOverride = new GUIContent("Center Color", "The Override Color of the ProximityLight Gradient at the Center (RGB) and (A) is Gradient Extent");
        public static GUIContent proximityLightMiddleColorOverride = new GUIContent("Middle Color", "The Override Color of the ProximityLight Gradient at the Middle (RGB) and (A) is Gradient Extent");
        public static GUIContent proximityLightOuterColorOverride = new GUIContent("Outer Color", "The Override Color of the ProximityLight Gradient at the Outer Edge (RGB) and (A) is Gradient Extent");
        public static GUIContent proximityLightSubtractive = new GUIContent("Subtractive", "Proximity Lights Remove Light from a Surface, Used to Mimic a Shadow");
        public static GUIContent proximityLightTwoSided = new GUIContent("Two Sided", "Proximity Lights Apply to Both Sides of a Surface");
        public static GUIContent fluentLightIntensity = new GUIContent("Light Intensity", "Intensity Scaler for All Hover and Proximity Lights");
        public static GUIContent roundCorners = new GUIContent("Round Corners", "(Assumes UVs Specify Borders of Surface, Works Best on Unity Cube, Quad, and Plane)");
        public static GUIContent roundCornerRadius = new GUIContent("Unit Radius", "Rounded Rectangle Corner Unit Sphere Radius");
        public static GUIContent roundCornerMargin = new GUIContent("Margin %", "Distance From Geometry Edge");
        public static GUIContent borderLight = new GUIContent("Border Light", "Enable Border Lighting (Assumes UVs Specify Borders of Surface, Works Best on Unity Cube, Quad, and Plane)");
        public static GUIContent borderLightUsesHoverColor = new GUIContent("Use Hover Color", "Border Color Comes From Hover Light Color Override");
        public static GUIContent borderLightReplacesAlbedo = new GUIContent("Replace Albedo", "Border Light Replaces Albedo (Replacement Rather Than Additive)");
        public static GUIContent borderLightOpaque = new GUIContent("Opaque Borders", "Borders Override Alpha Value to Appear Opaque");
        public static GUIContent borderWidth = new GUIContent("Width %", "Uniform Width Along Border as a % of the Smallest XYZ Dimension");
        public static GUIContent borderMinValue = new GUIContent("Brightness", "Brightness Scaler");
        public static GUIContent edgeSmoothingValue = new GUIContent("Edge Smoothing Value", "Smooths Edges When Round Corners and Transparency Is Enabled");
        public static GUIContent borderLightOpaqueAlpha = new GUIContent("Alpha", "Alpha value of \"opaque\" borders.");
        public static GUIContent innerGlow = new GUIContent("Inner Glow", "Enable Inner Glow (Assumes UVs Specify Borders of Surface, Works Best on Unity Cube, Quad, and Plane)");
        public static GUIContent innerGlowColor = new GUIContent("Color", "Inner Glow Color (RGB) and Intensity (A)");
        public static GUIContent innerGlowPower = new GUIContent("Power", "Power Exponent to Control Glow");
        public static GUIContent iridescence = new GUIContent("Iridescence", "Simulated Iridescence via Albedo Changes with the Angle of Observation)");
        public static GUIContent iridescentSpectrumMap = new GUIContent("Spectrum Map", "Spectrum of Colors to Apply (Usually a Texture with ROYGBIV from Left to Right)");
        public static GUIContent iridescenceIntensity = new GUIContent("Intensity", "Intensity of Iridescence");
        public static GUIContent iridescenceThreshold = new GUIContent("Threshold", "Threshold Window to Sample From the Spectrum Map");
        public static GUIContent iridescenceAngle = new GUIContent("Angle", "Surface Angle");
        public static GUIContent environmentColoring = new GUIContent("Environment Coloring", "Change Color Based on View");
        public static GUIContent environmentColorThreshold = new GUIContent("Threshold", "Threshold When Environment Coloring Should Appear Based on Surface Normal");
        public static GUIContent environmentColorIntensity = new GUIContent("Intensity", "Intensity (or Brightness) of the Environment Coloring");
        public static GUIContent environmentColorX = new GUIContent("X-Axis Color", "Color Along the World Space X-Axis");
        public static GUIContent environmentColorY = new GUIContent("Y-Axis Color", "Color Along the World Space Y-Axis");
        public static GUIContent environmentColorZ = new GUIContent("Z-Axis Color", "Color Along the World Space Z-Axis");
        public static GUIContent stencil = new GUIContent("Enable Stencil Testing", "Enabled Stencil Testing Operations");
        public static GUIContent stencilReference = new GUIContent("Stencil Reference", "Value to Compared Against (if Comparison is Anything but Always) and/or the Value to be Written to the Buffer (if Either Pass, Fail or ZFail is Set to Replace)");
        public static GUIContent stencilComparison = new GUIContent("Stencil Comparison", "Function to Compare the Reference Value to");
        public static GUIContent stencilOperation = new GUIContent("Stencil Operation", "What to do When the Stencil Test Passes");
        public static GUIContent ignoreZScale = new GUIContent("Ignore Z Scale", "For Features That Use Object Scale (Round Corners, Border Light, etc.), Ignore the Z Scale of the Object");
    }

    protected MaterialProperty instancedColor;
    protected MaterialProperty albedoMap;
    protected MaterialProperty albedoColor;
    protected MaterialProperty albedoAlphaMode;
    protected MaterialProperty albedoAssignedAtRuntime;
    protected MaterialProperty alphaCutoff;
    protected MaterialProperty enableChannelMap;
    protected MaterialProperty channelMap;
    protected MaterialProperty enableNormalMap;
    protected MaterialProperty normalMap;
    protected MaterialProperty normalMapScale;
    protected MaterialProperty enableEmission;
    protected MaterialProperty emissiveColor;
    protected MaterialProperty enableTriplanarMapping;
    protected MaterialProperty enableLocalSpaceTriplanarMapping;
    protected MaterialProperty triplanarMappingBlendSharpness;
    protected MaterialProperty metallic;
    protected MaterialProperty smoothness;
    protected MaterialProperty directionalLight;
    protected MaterialProperty specularHighlights;
    protected MaterialProperty sphericalHarmonics;
    protected MaterialProperty reflections;
    protected MaterialProperty refraction;
    protected MaterialProperty refractiveIndex;
    protected MaterialProperty rimLight;
    protected MaterialProperty rimColor;
    protected MaterialProperty rimPower;
    protected MaterialProperty vertexColors;
    protected MaterialProperty vertexExtrusion;
    protected MaterialProperty vertexExtrusionValue;
    protected MaterialProperty blendedClippingWidth;
    protected MaterialProperty clippingBorder;
    protected MaterialProperty clippingBorderWidth;
    protected MaterialProperty clippingBorderColor;
    protected MaterialProperty nearPlaneFade;
    protected MaterialProperty nearLightFade;
    protected MaterialProperty fadeBeginDistance;
    protected MaterialProperty fadeCompleteDistance;
    protected MaterialProperty fadeMinValue;
    protected MaterialProperty hoverLight;
    protected MaterialProperty enableHoverColorOverride;
    protected MaterialProperty hoverColorOverride;
    protected MaterialProperty proximityLight;
    protected MaterialProperty enableProximityLightColorOverride;
    protected MaterialProperty proximityLightCenterColorOverride;
    protected MaterialProperty proximityLightMiddleColorOverride;
    protected MaterialProperty proximityLightOuterColorOverride;
    protected MaterialProperty proximityLightSubtractive;
    protected MaterialProperty proximityLightTwoSided;
    protected MaterialProperty fluentLightIntensity;
    protected MaterialProperty roundCorners;
    protected MaterialProperty roundCornerRadius;
    protected MaterialProperty roundCornerMargin;
    protected MaterialProperty borderLight;
    protected MaterialProperty borderLightUsesHoverColor;
    protected MaterialProperty borderLightReplacesAlbedo;
    protected MaterialProperty borderLightOpaque;
    protected MaterialProperty borderWidth;
    protected MaterialProperty borderMinValue;
    protected MaterialProperty edgeSmoothingValue;
    protected MaterialProperty borderLightOpaqueAlpha;
    protected MaterialProperty innerGlow;
    protected MaterialProperty innerGlowColor;
    protected MaterialProperty innerGlowPower;
    protected MaterialProperty iridescence;
    protected MaterialProperty iridescentSpectrumMap;
    protected MaterialProperty iridescenceIntensity;
    protected MaterialProperty iridescenceThreshold;
    protected MaterialProperty iridescenceAngle;
    protected MaterialProperty environmentColoring;
    protected MaterialProperty environmentColorThreshold;
    protected MaterialProperty environmentColorIntensity;
    protected MaterialProperty environmentColorX;
    protected MaterialProperty environmentColorY;
    protected MaterialProperty environmentColorZ;
    protected MaterialProperty stencil;
    protected MaterialProperty stencilReference;
    protected MaterialProperty stencilComparison;
    protected MaterialProperty stencilOperation;
    protected MaterialProperty ignoreZScale;

    protected override void FindProperties(MaterialProperty[] props) {
        base.FindProperties(props);

        instancedColor = FindProperty(Styles.instancedColorName, props);
        albedoMap = FindProperty("_MainTex", props);
        albedoColor = FindProperty("_Color", props);
        albedoAlphaMode = FindProperty("_AlbedoAlphaMode", props);
        albedoAssignedAtRuntime = FindProperty("_AlbedoAssignedAtRuntime", props);
        alphaCutoff = FindProperty("_Cutoff", props);
        metallic = FindProperty("_Metallic", props);
        smoothness = FindProperty("_Smoothness", props);
        enableChannelMap = FindProperty("_EnableChannelMap", props);
        channelMap = FindProperty("_ChannelMap", props);
        enableNormalMap = FindProperty("_EnableNormalMap", props);
        normalMap = FindProperty("_NormalMap", props);
        normalMapScale = FindProperty("_NormalMapScale", props);
        enableEmission = FindProperty("_EnableEmission", props);
        emissiveColor = FindProperty("_EmissiveColor", props);
        enableTriplanarMapping = FindProperty("_EnableTriplanarMapping", props);
        enableLocalSpaceTriplanarMapping = FindProperty("_EnableLocalSpaceTriplanarMapping", props);
        triplanarMappingBlendSharpness = FindProperty("_TriplanarMappingBlendSharpness", props);
        directionalLight = FindProperty("_DirectionalLight", props);
        specularHighlights = FindProperty("_SpecularHighlights", props);
        sphericalHarmonics = FindProperty("_SphericalHarmonics", props);
        reflections = FindProperty("_Reflections", props);
        refraction = FindProperty("_Refraction", props);
        refractiveIndex = FindProperty("_RefractiveIndex", props);
        rimLight = FindProperty("_RimLight", props);
        rimColor = FindProperty("_RimColor", props);
        rimPower = FindProperty("_RimPower", props);
        vertexColors = FindProperty("_VertexColors", props);
        vertexExtrusion = FindProperty("_VertexExtrusion", props);
        vertexExtrusionValue = FindProperty("_VertexExtrusionValue", props);
        blendedClippingWidth = FindProperty("_BlendedClippingWidth", props);
        clippingBorder = FindProperty("_ClippingBorder", props);
        clippingBorderWidth = FindProperty("_ClippingBorderWidth", props);
        clippingBorderColor = FindProperty("_ClippingBorderColor", props);
        nearPlaneFade = FindProperty("_NearPlaneFade", props);
        nearLightFade = FindProperty("_NearLightFade", props);
        fadeBeginDistance = FindProperty("_FadeBeginDistance", props);
        fadeCompleteDistance = FindProperty("_FadeCompleteDistance", props);
        fadeMinValue = FindProperty("_FadeMinValue", props);
        hoverLight = FindProperty("_HoverLight", props);
        enableHoverColorOverride = FindProperty("_EnableHoverColorOverride", props);
        hoverColorOverride = FindProperty("_HoverColorOverride", props);
        proximityLight = FindProperty("_ProximityLight", props);
        enableProximityLightColorOverride = FindProperty("_EnableProximityLightColorOverride", props);
        proximityLightCenterColorOverride = FindProperty("_ProximityLightCenterColorOverride", props);
        proximityLightMiddleColorOverride = FindProperty("_ProximityLightMiddleColorOverride", props);
        proximityLightOuterColorOverride = FindProperty("_ProximityLightOuterColorOverride", props);
        proximityLightSubtractive = FindProperty("_ProximityLightSubtractive", props);
        proximityLightTwoSided = FindProperty("_ProximityLightTwoSided", props);
        fluentLightIntensity = FindProperty("_FluentLightIntensity", props);
        roundCorners = FindProperty("_RoundCorners", props);
        roundCornerRadius = FindProperty("_RoundCornerRadius", props);
        roundCornerMargin = FindProperty("_RoundCornerMargin", props);
        borderLight = FindProperty("_BorderLight", props);
        borderLightUsesHoverColor = FindProperty("_BorderLightUsesHoverColor", props);
        borderLightReplacesAlbedo = FindProperty("_BorderLightReplacesAlbedo", props);
        borderLightOpaque = FindProperty("_BorderLightOpaque", props);
        borderWidth = FindProperty("_BorderWidth", props);
        borderMinValue = FindProperty("_BorderMinValue", props);
        edgeSmoothingValue = FindProperty("_EdgeSmoothingValue", props);
        borderLightOpaqueAlpha = FindProperty("_BorderLightOpaqueAlpha", props);
        innerGlow = FindProperty("_InnerGlow", props);
        innerGlowColor = FindProperty("_InnerGlowColor", props);
        innerGlowPower = FindProperty("_InnerGlowPower", props);
        iridescence = FindProperty("_Iridescence", props);
        iridescentSpectrumMap = FindProperty("_IridescentSpectrumMap", props);
        iridescenceIntensity = FindProperty("_IridescenceIntensity", props);
        iridescenceThreshold = FindProperty("_IridescenceThreshold", props);
        iridescenceAngle = FindProperty("_IridescenceAngle", props);
        environmentColoring = FindProperty("_EnvironmentColoring", props);
        environmentColorThreshold = FindProperty("_EnvironmentColorThreshold", props);
        environmentColorIntensity = FindProperty("_EnvironmentColorIntensity", props);
        environmentColorX = FindProperty("_EnvironmentColorX", props);
        environmentColorY = FindProperty("_EnvironmentColorY", props);
        environmentColorZ = FindProperty("_EnvironmentColorZ", props);
        stencil = FindProperty("_Stencil", props);
        stencilReference = FindProperty("_StencilReference", props);
        stencilComparison = FindProperty(Styles.stencilComparisonName, props);
        stencilOperation = FindProperty(Styles.stencilOperationName, props);
        ignoreZScale = FindProperty("_IgnoreZScale", props);
    }

    public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) {
        Material material = (Material)materialEditor.target;

        base.OnGUI(materialEditor, props);

        MainMapOptions(materialEditor, material);
        RenderingOptions(materialEditor, material);
        FluentOptions(materialEditor, material);
        AdvancedOptions(materialEditor, material);
    }

    public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) {
        // Cache old shader properties with potentially different names than the new shader.
        float? smoothness = GetFloatProperty(material, "_Glossiness");
        float? diffuse = GetFloatProperty(material, "_UseDiffuse");
        float? specularHighlights = GetFloatProperty(material, "_SpecularHighlights");
        float? normalMap = null;
        Texture normalMapTexture = material.GetTexture("_BumpMap");
        float? normalMapScale = GetFloatProperty(material, "_BumpScale");
        float? emission = null;
        Color? emissionColor = GetColorProperty(material, "_EmissionColor");
        float? reflections = null;
        float? rimLighting = null;
        Vector4? textureScaleOffset = null;
        float? cullMode = GetFloatProperty(material, "_Cull");

        if(oldShader) {
            if(oldShader.name.Contains("Standard")) {
                normalMap = material.IsKeywordEnabled("_NORMALMAP") ? 1.0f : 0.0f;
                emission = material.IsKeywordEnabled("_EMISSION") ? 1.0f : 0.0f;
                reflections = GetFloatProperty(material, "_GlossyReflections");
            } else if(oldShader.name.Contains("Fast Configurable")) {
                normalMap = material.IsKeywordEnabled("_USEBUMPMAP_ON") ? 1.0f : 0.0f;
                emission = GetFloatProperty(material, "_UseEmissionColor");
                reflections = GetFloatProperty(material, "_UseReflections");
                rimLighting = GetFloatProperty(material, "_UseRimLighting");
                textureScaleOffset = GetVectorProperty(material, "_TextureScaleOffset");
            }
        }

        base.AssignNewShaderToMaterial(material, oldShader, newShader);

        // Apply old shader properties to the new shader.
        SetShaderFeatureActive(material, null, "_Smoothness", smoothness);
        SetShaderFeatureActive(material, "_DIRECTIONAL_LIGHT", "_DirectionalLight", diffuse);
        SetShaderFeatureActive(material, "_SPECULAR_HIGHLIGHTS", "_SpecularHighlights", specularHighlights);
        SetShaderFeatureActive(material, "_NORMAL_MAP", "_EnableNormalMap", normalMap);

        if(normalMapTexture) {
            material.SetTexture("_NormalMap", normalMapTexture);
        }

        SetShaderFeatureActive(material, null, "_NormalMapScale", normalMapScale);
        SetShaderFeatureActive(material, "_EMISSION", "_EnableEmission", emission);
        SetColorProperty(material, "_EmissiveColor", emissionColor);
        SetShaderFeatureActive(material, "_REFLECTIONS", "_Reflections", reflections);
        SetShaderFeatureActive(material, "_RIM_LIGHT", "_RimLight", rimLighting);
        SetVectorProperty(material, "_MainTex_ST", textureScaleOffset);
        SetShaderFeatureActive(material, null, "_CullMode", cullMode);

        // Setup the rendering mode based on the old shader.
        if(oldShader == null || !oldShader.name.Contains(LegacyShadersPath)) {
            SetupMaterialWithRenderingMode(material, (RenderingMode)material.GetFloat(BaseStyles.renderingModeName), CustomRenderingMode.Opaque, -1);
        } else {
            RenderingMode mode = RenderingMode.Opaque;

            if(oldShader.name.Contains(TransparentCutoutShadersPath)) {
                mode = RenderingMode.TransparentCutout;
            } else if(oldShader.name.Contains(TransparentShadersPath)) {
                mode = RenderingMode.Transparent;
            }

            material.SetFloat(BaseStyles.renderingModeName, (float)mode);

            MaterialChanged(material);
        }
    }

    protected override void MaterialChanged(Material material) {
        SetupMaterialWithAlbedo(material, albedoMap, albedoAlphaMode, albedoAssignedAtRuntime);

        base.MaterialChanged(material);
    }

    protected void MainMapOptions(MaterialEditor materialEditor, Material material) {
        GUILayout.Label(Styles.primaryMapsTitle, EditorStyles.boldLabel);

        materialEditor.TexturePropertySingleLine(Styles.albedo, albedoMap, albedoColor);

        if(albedoMap.textureValue == null) {
            materialEditor.ShaderProperty(albedoAssignedAtRuntime, Styles.albedoAssignedAtRuntime, 2);
        }

        materialEditor.ShaderProperty(enableChannelMap, Styles.enableChannelMap);

        if(PropertyEnabled(enableChannelMap)) {
            EditorGUI.indentLevel += 2;
            materialEditor.TexturePropertySingleLine(Styles.channelMap, channelMap);
            GUILayout.Box("Metallic (Red), Occlusion (Green), Emission (Blue), Smoothness (Alpha)", EditorStyles.helpBox, new GUILayoutOption[0]);
            EditorGUI.indentLevel -= 2;
        }

        if(!PropertyEnabled(enableChannelMap)) {
            EditorGUI.indentLevel += 2;

            albedoAlphaMode.floatValue = EditorGUILayout.Popup(albedoAlphaMode.displayName, (int)albedoAlphaMode.floatValue, Styles.albedoAlphaModeNames);

            if((RenderingMode)renderingMode.floatValue == RenderingMode.TransparentCutout ||
                (RenderingMode)renderingMode.floatValue == RenderingMode.Custom) {
                materialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoff.text);
            }

            if((AlbedoAlphaMode)albedoAlphaMode.floatValue != AlbedoAlphaMode.Metallic) {
                materialEditor.ShaderProperty(metallic, Styles.metallic);
            }

            if((AlbedoAlphaMode)albedoAlphaMode.floatValue != AlbedoAlphaMode.Smoothness) {
                materialEditor.ShaderProperty(smoothness, Styles.smoothness);
            }

            SetupMaterialWithAlbedo(material, albedoMap, albedoAlphaMode, albedoAssignedAtRuntime);

            EditorGUI.indentLevel -= 2;
        }

        if(PropertyEnabled(directionalLight) ||
            PropertyEnabled(reflections) ||
            PropertyEnabled(rimLight) ||
            PropertyEnabled(environmentColoring)) {
            materialEditor.ShaderProperty(enableNormalMap, Styles.enableNormalMap);

            if(PropertyEnabled(enableNormalMap)) {
                EditorGUI.indentLevel += 2;
                materialEditor.TexturePropertySingleLine(Styles.normalMap, normalMap, normalMap.textureValue != null ? normalMapScale : null);
                EditorGUI.indentLevel -= 2;
            }
        }

        materialEditor.ShaderProperty(enableEmission, Styles.enableEmission);

        if(PropertyEnabled(enableEmission)) {
            materialEditor.ShaderProperty(emissiveColor, Styles.emissiveColor, 2);
        }

        materialEditor.ShaderProperty(enableTriplanarMapping, Styles.enableTriplanarMapping);

        if(PropertyEnabled(enableTriplanarMapping)) {
            materialEditor.ShaderProperty(enableLocalSpaceTriplanarMapping, Styles.enableLocalSpaceTriplanarMapping, 2);
            materialEditor.ShaderProperty(triplanarMappingBlendSharpness, Styles.triplanarMappingBlendSharpness, 2);
        }

        EditorGUILayout.Space();
        materialEditor.TextureScaleOffsetProperty(albedoMap);
    }

    protected void RenderingOptions(MaterialEditor materialEditor, Material material) {
        EditorGUILayout.Space();
        GUILayout.Label(Styles.renderingOptionsTitle, EditorStyles.boldLabel);

        materialEditor.ShaderProperty(directionalLight, Styles.directionalLight);

        if(PropertyEnabled(directionalLight)) {
            materialEditor.ShaderProperty(specularHighlights, Styles.specularHighlights, 2);
        }

        materialEditor.ShaderProperty(sphericalHarmonics, Styles.sphericalHarmonics);

        materialEditor.ShaderProperty(reflections, Styles.reflections);

        if(PropertyEnabled(reflections)) {
            materialEditor.ShaderProperty(refraction, Styles.refraction, 2);

            if(PropertyEnabled(refraction)) {
                materialEditor.ShaderProperty(refractiveIndex, Styles.refractiveIndex, 4);
            }
        }

        materialEditor.ShaderProperty(rimLight, Styles.rimLight);

        if(PropertyEnabled(rimLight)) {
            materialEditor.ShaderProperty(rimColor, Styles.rimColor, 2);
            materialEditor.ShaderProperty(rimPower, Styles.rimPower, 2);
        }

        materialEditor.ShaderProperty(vertexColors, Styles.vertexColors);

        materialEditor.ShaderProperty(vertexExtrusion, Styles.vertexExtrusion);

        if(PropertyEnabled(vertexExtrusion)) {
            materialEditor.ShaderProperty(vertexExtrusionValue, Styles.vertexExtrusionValue, 2);
        }

        if((RenderingMode)renderingMode.floatValue != RenderingMode.Opaque &&
            (RenderingMode)renderingMode.floatValue != RenderingMode.TransparentCutout) {
            materialEditor.ShaderProperty(blendedClippingWidth, Styles.blendedClippingWidth);
            //  GUILayout.Box(string.Format(Styles.propertiesComponentHelp, nameof(ClippingPrimitive), "other clipping"), EditorStyles.helpBox, new GUILayoutOption[0]);
        }

        materialEditor.ShaderProperty(clippingBorder, Styles.clippingBorder);

        if(PropertyEnabled(clippingBorder)) {
            materialEditor.ShaderProperty(clippingBorderWidth, Styles.clippingBorderWidth, 2);
            materialEditor.ShaderProperty(clippingBorderColor, Styles.clippingBorderColor, 2);
            //   GUILayout.Box(string.Format(Styles.propertiesComponentHelp, nameof(ClippingPrimitive), "other clipping"), EditorStyles.helpBox, new GUILayoutOption[0]);
        }

        materialEditor.ShaderProperty(nearPlaneFade, Styles.nearPlaneFade);

        if(PropertyEnabled(nearPlaneFade)) {
            materialEditor.ShaderProperty(nearLightFade, Styles.nearLightFade, 2);
            materialEditor.ShaderProperty(fadeBeginDistance, Styles.fadeBeginDistance, 2);
            materialEditor.ShaderProperty(fadeCompleteDistance, Styles.fadeCompleteDistance, 2);
            materialEditor.ShaderProperty(fadeMinValue, Styles.fadeMinValue, 2);
        }
    }

    protected void FluentOptions(MaterialEditor materialEditor, Material material) {
        EditorGUILayout.Space();
        GUILayout.Label(Styles.fluentOptionsTitle, EditorStyles.boldLabel);
        RenderingMode mode = (RenderingMode)renderingMode.floatValue;
        CustomRenderingMode customMode = (CustomRenderingMode)customRenderingMode.floatValue;

        materialEditor.ShaderProperty(hoverLight, Styles.hoverLight);

        if(PropertyEnabled(hoverLight)) {
            //  GUILayout.Box(string.Format(Styles.propertiesComponentHelp, nameof(HoverLight), Styles.hoverLight.text), EditorStyles.helpBox, new GUILayoutOption[0]);

            materialEditor.ShaderProperty(enableHoverColorOverride, Styles.enableHoverColorOverride, 2);

            if(PropertyEnabled(enableHoverColorOverride)) {
                materialEditor.ShaderProperty(hoverColorOverride, Styles.hoverColorOverride, 4);
            }
        }

        materialEditor.ShaderProperty(proximityLight, Styles.proximityLight);

        if(PropertyEnabled(proximityLight)) {
            materialEditor.ShaderProperty(enableProximityLightColorOverride, Styles.enableProximityLightColorOverride, 2);

            if(PropertyEnabled(enableProximityLightColorOverride)) {
                materialEditor.ShaderProperty(proximityLightCenterColorOverride, Styles.proximityLightCenterColorOverride, 4);
                materialEditor.ShaderProperty(proximityLightMiddleColorOverride, Styles.proximityLightMiddleColorOverride, 4);
                materialEditor.ShaderProperty(proximityLightOuterColorOverride, Styles.proximityLightOuterColorOverride, 4);
            }

            materialEditor.ShaderProperty(proximityLightSubtractive, Styles.proximityLightSubtractive, 2);
            materialEditor.ShaderProperty(proximityLightTwoSided, Styles.proximityLightTwoSided, 2);
            //   GUILayout.Box(string.Format(Styles.propertiesComponentHelp, nameof(ProximityLight), Styles.proximityLight.text), EditorStyles.helpBox, new GUILayoutOption[0]);
        }

        materialEditor.ShaderProperty(borderLight, Styles.borderLight);

        if(PropertyEnabled(borderLight)) {
            materialEditor.ShaderProperty(borderWidth, Styles.borderWidth, 2);

            materialEditor.ShaderProperty(borderMinValue, Styles.borderMinValue, 2);

            materialEditor.ShaderProperty(borderLightReplacesAlbedo, Styles.borderLightReplacesAlbedo, 2);

            if(PropertyEnabled(hoverLight) && PropertyEnabled(enableHoverColorOverride)) {
                materialEditor.ShaderProperty(borderLightUsesHoverColor, Styles.borderLightUsesHoverColor, 2);
            }

            if(mode == RenderingMode.TransparentCutout || mode == RenderingMode.Transparent ||
                (mode == RenderingMode.Custom && customMode == CustomRenderingMode.TransparentCutout) ||
                (mode == RenderingMode.Custom && customMode == CustomRenderingMode.Transparent)) {
                materialEditor.ShaderProperty(borderLightOpaque, Styles.borderLightOpaque, 2);

                if(PropertyEnabled(borderLightOpaque)) {
                    materialEditor.ShaderProperty(borderLightOpaqueAlpha, Styles.borderLightOpaqueAlpha, 4);
                }
            }
        }

        if(PropertyEnabled(hoverLight) || PropertyEnabled(proximityLight) || PropertyEnabled(borderLight)) {
            materialEditor.ShaderProperty(fluentLightIntensity, Styles.fluentLightIntensity);
        }

        materialEditor.ShaderProperty(roundCorners, Styles.roundCorners);

        if(PropertyEnabled(roundCorners)) {
            materialEditor.ShaderProperty(roundCornerRadius, Styles.roundCornerRadius, 2);
            materialEditor.ShaderProperty(roundCornerMargin, Styles.roundCornerMargin, 2);
        }

        if(PropertyEnabled(roundCorners) || PropertyEnabled(borderLight)) {
            materialEditor.ShaderProperty(edgeSmoothingValue, Styles.edgeSmoothingValue);
        }

        materialEditor.ShaderProperty(innerGlow, Styles.innerGlow);

        if(PropertyEnabled(innerGlow)) {
            materialEditor.ShaderProperty(innerGlowColor, Styles.innerGlowColor, 2);
            materialEditor.ShaderProperty(innerGlowPower, Styles.innerGlowPower, 2);
        }

        materialEditor.ShaderProperty(iridescence, Styles.iridescence);

        if(PropertyEnabled(iridescence)) {
            EditorGUI.indentLevel += 2;
            materialEditor.TexturePropertySingleLine(Styles.iridescentSpectrumMap, iridescentSpectrumMap);
            EditorGUI.indentLevel -= 2;
            materialEditor.ShaderProperty(iridescenceIntensity, Styles.iridescenceIntensity, 2);
            materialEditor.ShaderProperty(iridescenceThreshold, Styles.iridescenceThreshold, 2);
            materialEditor.ShaderProperty(iridescenceAngle, Styles.iridescenceAngle, 2);
        }

        materialEditor.ShaderProperty(environmentColoring, Styles.environmentColoring);

        if(PropertyEnabled(environmentColoring)) {
            materialEditor.ShaderProperty(environmentColorThreshold, Styles.environmentColorThreshold, 2);
            materialEditor.ShaderProperty(environmentColorIntensity, Styles.environmentColorIntensity, 2);
            materialEditor.ShaderProperty(environmentColorX, Styles.environmentColorX, 2);
            materialEditor.ShaderProperty(environmentColorY, Styles.environmentColorY, 2);
            materialEditor.ShaderProperty(environmentColorZ, Styles.environmentColorZ, 2);
        }
    }

    protected void AdvancedOptions(MaterialEditor materialEditor, Material material) {
        EditorGUILayout.Space();
        GUILayout.Label(Styles.advancedOptionsTitle, EditorStyles.boldLabel);

        EditorGUI.BeginChangeCheck();

        materialEditor.ShaderProperty(renderQueueOverride, BaseStyles.renderQueueOverride);

        if(EditorGUI.EndChangeCheck()) {
            MaterialChanged(material);
        }

        // Show the RenderQueueField but do not allow users to directly manipulate it. That is done via the renderQueueOverride.
        GUI.enabled = false;
        materialEditor.RenderQueueField();

        // Enable instancing to disable batching. Static and dynamic batching will normalize the object scale, which breaks 
        // features which utilize object scale.
        GUI.enabled = !ScaleRequired();

        if(!GUI.enabled && !material.enableInstancing) {
            material.enableInstancing = true;
        }

        materialEditor.EnableInstancingField();

        if(material.enableInstancing) {
            GUI.enabled = true;
            materialEditor.ShaderProperty(instancedColor, Styles.instancedColor, 2);
        } else {
            // When instancing is disable, disable instanced color.
            SetShaderFeatureActive(material, Styles.instancedColorFeatureName, Styles.instancedColorName, 0.0f);
        }

        materialEditor.ShaderProperty(stencil, Styles.stencil);

        if(PropertyEnabled(stencil)) {
            materialEditor.ShaderProperty(stencilReference, Styles.stencilReference, 2);
            materialEditor.ShaderProperty(stencilComparison, Styles.stencilComparison, 2);
            materialEditor.ShaderProperty(stencilOperation, Styles.stencilOperation, 2);
        } else {
            // When stencil is disable, revert to the default stencil operations. Note, when tested on D3D11 hardware the stencil state 
            // is still set even when the CompareFunction.Disabled is selected, but this does not seem to affect performance.
            material.SetInt(Styles.stencilComparisonName, (int)CompareFunction.Disabled);
            material.SetInt(Styles.stencilOperationName, (int)StencilOp.Keep);
        }

        if(ScaleRequired()) {
            materialEditor.ShaderProperty(ignoreZScale, Styles.ignoreZScale);
        }
    }

    protected bool ScaleRequired() {
        return PropertyEnabled(roundCorners) ||
               PropertyEnabled(borderLight) ||
               (PropertyEnabled(enableTriplanarMapping) && PropertyEnabled(enableLocalSpaceTriplanarMapping));
    }

    protected static void SetupMaterialWithAlbedo(Material material, MaterialProperty albedoMap, MaterialProperty albedoAlphaMode, MaterialProperty albedoAssignedAtRuntime) {
        if(albedoMap.textureValue || PropertyEnabled(albedoAssignedAtRuntime)) {
            material.DisableKeyword(Styles.disableAlbedoMapName);
        } else {
            material.EnableKeyword(Styles.disableAlbedoMapName);
        }

        switch((AlbedoAlphaMode)albedoAlphaMode.floatValue) {
            case AlbedoAlphaMode.Transparency: {
                    material.DisableKeyword(Styles.albedoMapAlphaMetallicName);
                    material.DisableKeyword(Styles.albedoMapAlphaSmoothnessName);
                }
                break;

            case AlbedoAlphaMode.Metallic: {
                    material.EnableKeyword(Styles.albedoMapAlphaMetallicName);
                    material.DisableKeyword(Styles.albedoMapAlphaSmoothnessName);
                }
                break;

            case AlbedoAlphaMode.Smoothness: {
                    material.DisableKeyword(Styles.albedoMapAlphaMetallicName);
                    material.EnableKeyword(Styles.albedoMapAlphaSmoothnessName);
                }
                break;
        }
    }


    protected static void UpgradeShaderForLightweightRenderPipeline() {
        if(EditorUtility.DisplayDialog("Upgrade MRTK Standard Shader?",
                                        "This will alter the MRTK Standard Shader for use with Unity's Lightweight Render Pipeline. You cannot undo this action.",
                                        "Ok",
                                        "Cancel")) {
            string shaderName = "ShadowSDK/Standard";
            string path = AssetDatabase.GetAssetPath(Shader.Find(shaderName));

            if(!string.IsNullOrEmpty(path)) {
                try {
                    string upgradedShader = File.ReadAllText(path);
                    upgradedShader = upgradedShader.Replace("Tags{ \"RenderType\" = \"Opaque\" \"LightMode\" = \"ForwardBase\" }",
                                                            "Tags{ \"RenderType\" = \"Opaque\" \"LightMode\" = \"LightweightForward\" }");
                    upgradedShader = upgradedShader.Replace("//#define _LIGHTWEIGHT_RENDER_PIPELINE",
                                                            "#define _LIGHTWEIGHT_RENDER_PIPELINE");
                    File.WriteAllText(path, upgradedShader);
                    AssetDatabase.Refresh();

                    Debug.LogFormat("Upgraded {0} for use with the Lightweight Render Pipeline.", path);
                } catch(Exception e) {
                    Debug.LogException(e);
                }
            } else {
                Debug.LogErrorFormat("Failed to get asset path to: {0}", shaderName);
            }
        }
    }

    [MenuItem("ShadowSDK/Utilities/Upgrade MRTK Standard Shader for Lightweight Render Pipeline", true)]
    protected static bool UpgradeShaderForLightweightRenderPipelineValidate() {
        // If a scriptable render pipeline is not present, no need to upgrade the shader.
        return GraphicsSettings.renderPipelineAsset != null;
    }
}