﻿using UnityEditor;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;

namespace Mochie {

    public class SplatEditor : ShaderGUI {

        public static Dictionary<Material, Toggles> foldouts = new Dictionary<Material, Toggles>();
        Toggles toggles = new Toggles(new string[] {
            "Base Layer",
            "Layer 1 (R)",
            "Layer 2 (G)",
            "Layer 3 (B)",
            "Specularity",
            "Rain",
            "LTCGI",
            "AreaLit",
            "Lightmap Settings",
            "Debug"
        }, 4);

        string versionLabel = "v1.7.2";

        MaterialProperty _LayerBlendSource = null;
        MaterialProperty _LayerBlendMask = null;
        MaterialProperty _LayerBlendMaskBicubic = null;

        // Base layer
        MaterialProperty _Color0 = null;
        MaterialProperty _BaseColor0 = null;
        MaterialProperty _Normal0 = null;
        MaterialProperty _PackedMap0 = null;
        MaterialProperty _NormalStrength0 = null;
        MaterialProperty _Roughness0 = null;
        MaterialProperty _Metallic0 = null;
        MaterialProperty _Occlusion0 = null;
        MaterialProperty _Height0 = null;
        MaterialProperty _HeightOffset0 = null;
        MaterialProperty _ToggleStochastic0 = null;
        MaterialProperty _ToggleParallax0 = null;
        MaterialProperty _TogglePOM0 = null;
        // MaterialProperty _SmoothnessMode0 = null;
        MaterialProperty _RoughnessChannel0 = null;
        MaterialProperty _MetallicChannel0 = null;
        MaterialProperty _OcclusionChannel0 = null;
        MaterialProperty _HeightChannel0 = null;
        MaterialProperty _Layer0UVSet = null;
        MaterialProperty _PuddleHeightBlend0 = null;
        // MaterialProperty _PuddleRainMask0 = null;
        MaterialProperty _PuddleBlendMin0 = null;
        MaterialProperty _PuddleBlendMax0 = null;
        MaterialProperty _PuddleTint0 = null;

        // Layer 1
        MaterialProperty _ToggleLayer1 = null;
        MaterialProperty _Color1 = null;
        MaterialProperty _BaseColor1 = null;
        MaterialProperty _Normal1 = null;
        MaterialProperty _PackedMap1 = null;
        MaterialProperty _NormalStrength1 = null;
        MaterialProperty _Roughness1 = null;
        MaterialProperty _Metallic1 = null;
        MaterialProperty _Occlusion1 = null;
        MaterialProperty _Height1 = null;
        MaterialProperty _HeightOffset1 = null;
        MaterialProperty _ToggleStochastic1 = null;
        MaterialProperty _ToggleParallax1 = null;
        MaterialProperty _TogglePOM1 = null;
        // MaterialProperty _SmoothnessMode1 = null;
        MaterialProperty _RoughnessChannel1 = null;
        MaterialProperty _MetallicChannel1 = null;
        MaterialProperty _OcclusionChannel1 = null;
        MaterialProperty _HeightChannel1 = null;
        MaterialProperty _BlendThresholdMin1 = null;
        MaterialProperty _BlendThresholdMax1 = null;
        MaterialProperty _Layer1UVSet = null;
        MaterialProperty _HeightBlend1 = null;
        MaterialProperty _HeightBlendMin1 = null;
        MaterialProperty _HeightBlendMax1 = null;
        MaterialProperty _PuddleHeightBlend1 = null;
        // MaterialProperty _PuddleRainMask1 = null;
        MaterialProperty _PuddleBlendMin1 = null;
        MaterialProperty _PuddleBlendMax1 = null;
        MaterialProperty _PuddleTint1 = null;
        MaterialProperty _HeightBlendEdges1 = null;
        MaterialProperty _EdgePower1 = null;

        // Layer 2
        MaterialProperty _ToggleLayer2 = null;
        MaterialProperty _Color2 = null;
        MaterialProperty _BaseColor2 = null;
        MaterialProperty _Normal2 = null;
        MaterialProperty _PackedMap2 = null;
        MaterialProperty _NormalStrength2 = null;
        MaterialProperty _Roughness2 = null;
        MaterialProperty _Metallic2 = null;
        MaterialProperty _Occlusion2 = null;
        MaterialProperty _Height2 = null;
        MaterialProperty _HeightOffset2 = null;
        MaterialProperty _ToggleStochastic2 = null;
        MaterialProperty _ToggleParallax2 = null;
        MaterialProperty _TogglePOM2 = null;
        // MaterialProperty _SmoothnessMode2 = null;
        MaterialProperty _RoughnessChannel2 = null;
        MaterialProperty _MetallicChannel2 = null;
        MaterialProperty _OcclusionChannel2 = null;
        MaterialProperty _HeightChannel2 = null;
        MaterialProperty _BlendThresholdMin2 = null;
        MaterialProperty _BlendThresholdMax2 = null;
        MaterialProperty _Layer2UVSet = null;
        MaterialProperty _HeightBlend2 = null;
        MaterialProperty _HeightBlendMin2 = null;
        MaterialProperty _HeightBlendMax2 = null;
        MaterialProperty _PuddleHeightBlend2 = null;
        // MaterialProperty _PuddleRainMask2 = null;
        MaterialProperty _PuddleBlendMin2 = null;
        MaterialProperty _PuddleBlendMax2 = null;
        MaterialProperty _PuddleTint2 = null;
        MaterialProperty _HeightBlendEdges2 = null;
        MaterialProperty _EdgePower2 = null;

        // Layer 3
        MaterialProperty _ToggleLayer3 = null;
        MaterialProperty _Color3 = null;
        MaterialProperty _BaseColor3 = null;
        MaterialProperty _Normal3 = null;
        MaterialProperty _PackedMap3 = null;
        MaterialProperty _NormalStrength3 = null;
        MaterialProperty _Roughness3 = null;
        MaterialProperty _Metallic3 = null;
        MaterialProperty _Occlusion3 = null;
        MaterialProperty _Height3 = null;
        MaterialProperty _HeightOffset3 = null;
        MaterialProperty _ToggleStochastic3 = null;
        MaterialProperty _ToggleParallax3 = null;
        MaterialProperty _TogglePOM3 = null;
        // MaterialProperty _SmoothnessMode3 = null;
        MaterialProperty _RoughnessChannel3 = null;
        MaterialProperty _MetallicChannel3 = null;
        MaterialProperty _OcclusionChannel3 = null;
        MaterialProperty _HeightChannel3 = null;
        MaterialProperty _BlendThresholdMin3 = null;
        MaterialProperty _BlendThresholdMax3 = null;
        MaterialProperty _Layer3UVSet = null;
        MaterialProperty _HeightBlend3 = null;
        MaterialProperty _HeightBlendMin3 = null;
        MaterialProperty _HeightBlendMax3 = null;
        MaterialProperty _PuddleHeightBlend3 = null;
        // MaterialProperty _PuddleRainMask3 = null;
        MaterialProperty _PuddleBlendMin3 = null;
        MaterialProperty _PuddleBlendMax3 = null;
        MaterialProperty _PuddleTint3 = null;
        MaterialProperty _HeightBlendEdges3 = null;
        MaterialProperty _EdgePower3 = null;

        // Specularity
        MaterialProperty _ReflectionsToggle = null;
        MaterialProperty _SpecularHighlightsToggle = null;
        MaterialProperty _SpecularOcclusionToggle = null;
        MaterialProperty _SpecularOcclusionStrength = null;
        MaterialProperty _SpecularOcclusionContrast = null;
        MaterialProperty _SpecularOcclusionBrightness = null;
        MaterialProperty _SpecularOcclusionTint = null;
        MaterialProperty _SpecularOcclusionHDR = null;
        MaterialProperty _SpecularHighlightStrength = null;
        MaterialProperty _ReflectionStrength = null;
        MaterialProperty _FresnelToggle = null;
        MaterialProperty _FresnelStrength = null;
        MaterialProperty _SSRToggle = null;
        MaterialProperty _VRSSR = null;
        MaterialProperty _SSRStrength = null;
        MaterialProperty _SSREdgeFade = null;
        MaterialProperty _SSRHeight = null;
        MaterialProperty _GlobalRoughness = null;
        MaterialProperty _ContactHardening = null;
        MaterialProperty _LightVolumeSpecularity = null;
        MaterialProperty _LightVolumeSpecularityStrength = null;

        // Rain
        MaterialProperty _RainToggle = null;
        MaterialProperty _RainLayer0 = null;
        MaterialProperty _RainLayer1 = null;
        MaterialProperty _RainLayer2 = null;
        MaterialProperty _RainLayer3 = null;
        MaterialProperty _RainScale = null;
        MaterialProperty _RainSpeed = null;
        MaterialProperty _RainStrength = null;
        MaterialProperty _RainDensity = null;
        MaterialProperty _RainSize = null;
        MaterialProperty _RainThreshold = null;
        MaterialProperty _RainThresholdSize = null;
        // MaterialProperty _PuddleThreshold = null;
        // MaterialProperty _PuddleThresholdSize = null;
        MaterialProperty _RainPuddleDensity = null;
        MaterialProperty _RainPuddleSize = null;

        // Lightmapping Settings
        MaterialProperty _BAKERY_LMSPEC = null;
        MaterialProperty _BAKERY_SHNONLINEAR = null;
        MaterialProperty _BakeryMode = null;
        MaterialProperty _BicubicSampling = null;
        MaterialProperty _IgnoreRealtimeGI = null;
        MaterialProperty _BakeryLMSpecStrength = null;
        MaterialProperty _AdditiveLightVolumesToggle = null;

        // AreaLit
        MaterialProperty _AreaLitToggle = null;
        MaterialProperty _AreaLitStrength = null;
        MaterialProperty _AreaLitRoughnessMult = null;
        MaterialProperty _AreaLitSpecularOcclusion = null;
        MaterialProperty _LightMesh = null;
        MaterialProperty _LightTex0 = null;
        MaterialProperty _LightTex1 = null;
        MaterialProperty _LightTex2 = null;
        MaterialProperty _LightTex3 = null;
        MaterialProperty _OpaqueLights = null;
        MaterialProperty _AreaLitOcclusion = null;
        MaterialProperty _AreaLitOcclusionUVSet = null;

        // LTCGI
        MaterialProperty _LTCGI = null;
        MaterialProperty _LTCGIStrength = null;
        MaterialProperty _LTCGIRoughness = null;
        MaterialProperty _LTCGI_DiffuseColor = null;
        MaterialProperty _LTCGI_SpecularColor = null;

        // Debug Display
        MaterialProperty _DebugMode = null;
        MaterialProperty _DebugBaseColor = null;
        MaterialProperty _DebugNormals = null;
        MaterialProperty _DebugRoughness = null;
        MaterialProperty _DebugMetallic = null;
        MaterialProperty _DebugHeight = null;
        MaterialProperty _DebugVertexColors = null;
        MaterialProperty _DebugAtten = null;
        MaterialProperty _DebugReflections = null;
        MaterialProperty _DebugSpecular = null;
        MaterialProperty _DebugRainThreshold = null;
        MaterialProperty _DebugOcclusion = null;
        // MaterialProperty _DebugPuddleThreshold = null;

        MaterialEditor me;

        BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

        public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) {
            me = materialEditor;
            
            if (!me.isVisible)
                return;

            foreach (var property in GetType().GetFields(bindingFlags)){
                if (property.FieldType == typeof(MaterialProperty))
                    property.SetValue(this, FindProperty(property.Name, props));
            }
            Material mat = (Material)me.target;

            if (!foldouts.ContainsKey(mat))
                foldouts.Add(mat, toggles);
                
            if (mat.GetInt("_MaterialResetCheck") == 0){
                mat.SetInt("_MaterialResetCheck", 1);
                SetKeywords(mat);
                ApplySSRRenderSettings(mat);
            }

            MGUI.DoHeader("SPLAT MAPPING");
            
            EditorGUI.BeginChangeCheck(); {
                DoBlending();
                DoBaseLayer(mat);
                DoLayer1(mat);
                DoLayer2(mat);
                DoLayer3(mat);
                DoSpecularity(mat);
                DoRain(mat);
                DoLTCGI(mat);
                DoAreaLit(mat);
                DoLightmapSettings(mat);
                DoDebug(mat);
            }

            if (EditorGUI.EndChangeCheck()){
                SetKeywords(mat);
            }

            MGUI.Space10();
            MGUI.DoFooter(versionLabel);
        }

        void DoBlending(){
            MGUI.ShaderPropertyBold(me, _LayerBlendSource, "Layer Blending Source:");
            if (_LayerBlendSource.floatValue == 1){
                MGUI.PropertyGroupParent(()=>{
                    me.TexturePropertySingleLine(new GUIContent("Mask (RGBA)"), _LayerBlendMask, _LayerBlendMaskBicubic);
                    MGUI.TexPropLabel("Bicubic Sampling", 10, false);
                    MGUI.TextureSO(me, _LayerBlendMask);
                });
            }
            else {
                MGUI.Space6();
            }
        }

        void DoBaseLayer(Material mat){
            bool baseToggle = Foldouts.DoFoldout(foldouts, mat, "Base Layer", 1, Foldouts.Style.StandardButton);
            if (Foldouts.DoFoldoutButton(MGUI.collapseLabel, 11)) Toggles.CollapseFoldouts(mat, foldouts, 1);
            if (baseToggle) {
                MGUI.PropertyGroupParent(()=>{
                    MGUI.PropertyGroup(()=>{
                        me.TexturePropertySingleLine(Tips.albedoText, _BaseColor0, _Color0);
                        me.TexturePropertySingleLine(Tips.normalMapText, _Normal0, _Normal0.textureValue ? _NormalStrength0 : null);
                        MGUI.NormalWarning(_Normal0);
                        me.TexturePropertySingleLine(Tips.packedMapText, _PackedMap0);
                        MGUI.sRGBWarning(_PackedMap0);
                        MGUI.Space2();
                        me.ShaderProperty(_Layer0UVSet, "UV Set");
                        MGUI.TextureSO(me, _BaseColor0);
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_RoughnessChannel0, "Roughness Channel");
                        me.ShaderProperty(_MetallicChannel0, "Metallic Channel");
                        me.ShaderProperty(_OcclusionChannel0, "Occlusion Channel");
                        if (_ToggleParallax0.floatValue == 1 && _ToggleStochastic0.floatValue == 0){
                            me.ShaderProperty(_HeightChannel0, "Height Channel");
                        }
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_Roughness0, Tips.roughnessText);
                        me.ShaderProperty(_Metallic0, Tips.metallicText);
                        me.ShaderProperty(_Occlusion0, Tips.occlusionText);
                        if (_ToggleParallax0.floatValue == 1 && _ToggleStochastic0.floatValue == 0){
                            me.ShaderProperty(_Height0, Tips.heightMapPackedText);
                            me.ShaderProperty(_HeightOffset0, "Height Offset");
                        }
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_ToggleStochastic0, Tips.stochasticSamplingText);
                        MGUI.ToggleGroup(_ToggleStochastic0.floatValue == 1);
                        me.ShaderProperty(_ToggleParallax0, Tips.splatHeightText);
                        MGUI.ToggleGroupEnd();
                    });
                    if (_ToggleParallax0.floatValue == 1 && _ToggleStochastic0.floatValue == 0){
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_TogglePOM0, "Parallax Occlusion");
                            me.ShaderProperty(_PuddleHeightBlend0, "Puddle Height Blend");
                            if (_PuddleHeightBlend0.floatValue == 1){
                                MGUI.SliderMinMax01(_PuddleBlendMin0, _PuddleBlendMax0, "Puddle Blend Threshold ", 1);
                                me.ShaderProperty(_PuddleTint0, "Puddle Tint");
                            }
                        });
                    }
                });
            }
        }

        void DoLayer1(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _ToggleLayer1, "Layer 1 (R)", Foldouts.Style.StandardToggle)) {
                MGUI.ToggleGroup(_ToggleLayer1.floatValue == 0);
                MGUI.Space6();
                MGUI.SliderMinMax01(_BlendThresholdMin1, _BlendThresholdMax1, "Blend Threshold", 0);
                MGUI.PropertyGroupParent(() => {
                    MGUI.PropertyGroup(() => {
                        me.TexturePropertySingleLine(Tips.albedoText, _BaseColor1, _Color1);
                        me.TexturePropertySingleLine(Tips.normalMapText, _Normal1, _Normal1.textureValue ? _NormalStrength1 : null);
                        MGUI.NormalWarning(_Normal1);
                        me.TexturePropertySingleLine(Tips.packedMapText, _PackedMap1);
                        MGUI.sRGBWarning(_PackedMap1);
                        MGUI.Space2();
                        me.ShaderProperty(_Layer1UVSet, "UV Set");
                        MGUI.TextureSO(me, _BaseColor1);
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_RoughnessChannel1, "Roughness Channel");
                        me.ShaderProperty(_MetallicChannel1, "Metallic Channel");
                        me.ShaderProperty(_OcclusionChannel1, "Occlusion Channel");
                        if (_ToggleParallax1.floatValue == 1 && _ToggleStochastic1.floatValue == 0) {
                            me.ShaderProperty(_HeightChannel1, "Height Channel");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_Roughness1, Tips.roughnessText);
                        me.ShaderProperty(_Metallic1, Tips.metallicText);
                        me.ShaderProperty(_Occlusion1, Tips.occlusionText);
                        if (_ToggleParallax1.floatValue == 1 && _ToggleStochastic1.floatValue == 0) {
                            me.ShaderProperty(_Height1, Tips.heightMapPackedText);
                            me.ShaderProperty(_HeightOffset1, "Height Offset");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_ToggleStochastic1, Tips.stochasticSamplingText);
                        MGUI.ToggleGroup(_ToggleStochastic1.floatValue == 1);
                        me.ShaderProperty(_ToggleParallax1, Tips.splatHeightText);
                        MGUI.ToggleGroupEnd();
                    });
                    if (_ToggleParallax1.floatValue == 1 && _ToggleStochastic1.floatValue == 0) {
                        MGUI.PropertyGroup(() => {
                            me.ShaderProperty(_TogglePOM1, "Parallax Occlusion");
                            me.ShaderProperty(_HeightBlend1, "Layer Height Blend");
                            if (_HeightBlend1.floatValue == 1) {
                                MGUI.ToggleFloat(me, "Layer Edge Mask", _HeightBlendEdges1, _EdgePower1);
                                MGUI.SliderMinMax01(_HeightBlendMin1, _HeightBlendMax1, "Layer Blend Threshold ", 1);
                            }
                            me.ShaderProperty(_PuddleHeightBlend1, "Puddle Height Blend");
                            if (_PuddleHeightBlend1.floatValue == 1) {
                                MGUI.SliderMinMax01(_PuddleBlendMin1, _PuddleBlendMax1, "Puddle Blend Threshold ", 1);
                                me.ShaderProperty(_PuddleTint1, "Puddle Tint");
                            }
                        });
                    }
                });
                MGUI.ToggleGroupEnd();
            }
        }

        void DoLayer2(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _ToggleLayer2, "Layer 2 (G)", Foldouts.Style.StandardToggle)) {
                MGUI.ToggleGroup(_ToggleLayer2.floatValue == 0);
                MGUI.Space6();
                MGUI.SliderMinMax01(_BlendThresholdMin2, _BlendThresholdMax2, "Blend Threshold", 0);
                MGUI.PropertyGroupParent(() => {
                    MGUI.PropertyGroup(() => {
                        me.TexturePropertySingleLine(Tips.albedoText, _BaseColor2, _Color2);
                        me.TexturePropertySingleLine(Tips.normalMapText, _Normal2, _Normal2.textureValue ? _NormalStrength2 : null);
                        MGUI.NormalWarning(_Normal2);
                        me.TexturePropertySingleLine(Tips.packedMapText, _PackedMap2);
                        MGUI.sRGBWarning(_PackedMap2);
                        MGUI.Space2();
                        me.ShaderProperty(_Layer2UVSet, "UV Set");
                        MGUI.TextureSO(me, _BaseColor2);
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_RoughnessChannel2, "Roughness Channel");
                        me.ShaderProperty(_MetallicChannel2, "Metallic Channel");
                        me.ShaderProperty(_OcclusionChannel2, "Occlusion Channel");
                        if (_ToggleParallax2.floatValue == 1 && _ToggleStochastic2.floatValue == 0) {
                            me.ShaderProperty(_HeightChannel2, "Height Channel");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_Roughness2, Tips.roughnessText);
                        me.ShaderProperty(_Metallic2, Tips.metallicText);
                        me.ShaderProperty(_Occlusion2, Tips.occlusionText);
                        if (_ToggleParallax2.floatValue == 1 && _ToggleStochastic2.floatValue == 0) {
                            me.ShaderProperty(_Height2, Tips.heightMapPackedText);
                            me.ShaderProperty(_HeightOffset2, "Height Offset");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_ToggleStochastic2, Tips.stochasticSamplingText);
                        MGUI.ToggleGroup(_ToggleStochastic2.floatValue == 1);
                        me.ShaderProperty(_ToggleParallax2, Tips.splatHeightText);
                        MGUI.ToggleGroupEnd();
                    });
                    if (_ToggleParallax2.floatValue == 1 && _ToggleStochastic2.floatValue == 0) {
                        MGUI.PropertyGroup(() => {
                            me.ShaderProperty(_TogglePOM2, "Parallax Occlusion");
                            me.ShaderProperty(_HeightBlend2, "Layer Height Blend");
                            if (_HeightBlend2.floatValue == 1) {
                                MGUI.ToggleFloat(me, "Layer Edge Mask", _HeightBlendEdges2, _EdgePower2);
                                MGUI.SliderMinMax01(_HeightBlendMin2, _HeightBlendMax2, "Layer Blend Threshold ", 1);
                            }
                            me.ShaderProperty(_PuddleHeightBlend2, "Puddle Height Blend");
                            if (_PuddleHeightBlend2.floatValue == 1) {
                                MGUI.SliderMinMax01(_PuddleBlendMin2, _PuddleBlendMax2, "Puddle Blend Threshold ", 1);
                                me.ShaderProperty(_PuddleTint2, "Puddle Tint");
                            }
                        });
                    }
                });
                MGUI.ToggleGroupEnd();
            }
        }

        void DoLayer3(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _ToggleLayer3, "Layer 3 (B)", Foldouts.Style.StandardToggle)) {
                MGUI.ToggleGroup(_ToggleLayer3.floatValue == 0);
                MGUI.Space6();
                MGUI.SliderMinMax01(_BlendThresholdMin3, _BlendThresholdMax3, "Blend Threshold", 0);
                MGUI.PropertyGroupParent(() => {
                    MGUI.PropertyGroup(() => {
                        me.TexturePropertySingleLine(Tips.albedoText, _BaseColor3, _Color3);
                        me.TexturePropertySingleLine(Tips.normalMapText, _Normal3, _Normal3.textureValue ? _NormalStrength3 : null);
                        MGUI.NormalWarning(_Normal3);
                        me.TexturePropertySingleLine(Tips.packedMapText, _PackedMap3);
                        MGUI.sRGBWarning(_PackedMap3);
                        MGUI.Space2();
                        me.ShaderProperty(_Layer3UVSet, "UV Set");
                        MGUI.TextureSO(me, _BaseColor3);
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_RoughnessChannel3, "Roughness Channel");
                        me.ShaderProperty(_MetallicChannel3, "Metallic Channel");
                        me.ShaderProperty(_OcclusionChannel3, "Occlusion Channel");
                        if (_ToggleParallax3.floatValue == 1 && _ToggleStochastic3.floatValue == 0) {
                            me.ShaderProperty(_HeightChannel3, "Height Channel");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_Roughness3, Tips.roughnessText);
                        me.ShaderProperty(_Metallic3, Tips.metallicText);
                        me.ShaderProperty(_Occlusion3, Tips.occlusionText);
                        if (_ToggleParallax3.floatValue == 1 && _ToggleStochastic3.floatValue == 0) {
                            me.ShaderProperty(_Height3, Tips.heightMapPackedText);
                            me.ShaderProperty(_HeightOffset3, "Height Offset");
                        }
                    });
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_ToggleStochastic3, Tips.stochasticSamplingText);
                        MGUI.ToggleGroup(_ToggleStochastic3.floatValue == 1);
                        me.ShaderProperty(_ToggleParallax3, Tips.splatHeightText);
                        MGUI.ToggleGroupEnd();
                    });
                    if (_ToggleParallax3.floatValue == 1 && _ToggleStochastic3.floatValue == 0) {
                        MGUI.PropertyGroup(() => {
                            me.ShaderProperty(_TogglePOM3, "Parallax Occlusion");
                            me.ShaderProperty(_HeightBlend3, "Layer Height Blend");
                            if (_HeightBlend3.floatValue == 1) {
                                MGUI.ToggleFloat(me, "Layer Edge Mask", _HeightBlendEdges3, _EdgePower3);
                                MGUI.SliderMinMax01(_HeightBlendMin3, _HeightBlendMax3, "Layer Blend Threshold ", 1);
                            }
                            me.ShaderProperty(_PuddleHeightBlend3, "Puddle Height Blend");
                            if (_PuddleHeightBlend3.floatValue == 1) {
                                MGUI.SliderMinMax01(_PuddleBlendMin3, _PuddleBlendMax3, "Puddle Blend Threshold ", 1);
                                me.ShaderProperty(_PuddleTint3, "Puddle Tint");
                            }
                        });
                    }
                });
                MGUI.ToggleGroupEnd();
            }
        }

        void DoSpecularity(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, "Specularity", Foldouts.Style.Standard)){
                MGUI.PropertyGroupParent(()=>{
                    MGUI.PropertyGroup(()=>{
                        MGUI.ToggleFloat(me, Tips.specularHighlightsText, _SpecularHighlightsToggle, _SpecularHighlightStrength);
                        MGUI.ToggleFloat(me, Tips.cubemapReflectionsText, _ReflectionsToggle, _ReflectionStrength);
                        MGUI.ToggleFloat(me, Tips.lightVolumeSpecText, _LightVolumeSpecularity, _LightVolumeSpecularityStrength);
                        EditorGUI.BeginChangeCheck();
                        MGUI.ToggleFloat(me, Tips.ssrText, _SSRToggle, _SSRStrength);
                        if (EditorGUI.EndChangeCheck()){
                            ApplySSRRenderSettings(mat);
                        }
                        if (_SSRToggle.floatValue == 1){
                            MGUI.PropertyGroup(()=>{
                                me.ShaderProperty(_SSREdgeFade, Tips.ssrEdgeFadeText);
                                me.ShaderProperty(_SSRHeight, Tips.ssrDepthText);
                                me.ShaderProperty(_VRSSR, "Enable in VR");
                            });
                        }
                        MGUI.ToggleFloat(me, Tips.useFresnel, _FresnelToggle, _FresnelStrength);
                    });
                    MGUI.PropertyGroup(()=>{
                        MGUI.ToggleFloat(me, Tips.reflShadows, _SpecularOcclusionToggle, _SpecularOcclusionStrength);
                        if (_SpecularOcclusionToggle.floatValue == 1){
                            me.ShaderProperty(_SpecularOcclusionTint, "Lightmap Tint");
                            me.ShaderProperty(_SpecularOcclusionBrightness, "Lightmap Brightness");
                            me.ShaderProperty(_SpecularOcclusionContrast, "Lightmap Contrast");
                            me.ShaderProperty(_SpecularOcclusionHDR, "Lightmap HDR");
                        }
                        me.ShaderProperty(_GlobalRoughness, "Global Roughness");
                        me.ShaderProperty(_ContactHardening, Tips.contactHardeningText);
                        if (_SSRToggle.floatValue == 1){
                            MGUI.DisplayInfo("Screenspace reflections in VRChat require the \"Depth Light\" prefab found in: Assets/Mochie/Unity/Prefabs or at least one directional light with shadows enabled in the scene. \n\nThey can also be expensive, please use them sparingly!");
                        }
                    });
                });
            }
        }

        void DoRain(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _RainToggle, "Rain", Foldouts.Style.ThinLongToggle)){
                MGUI.ToggleGroup(_RainToggle.floatValue == 0);
                MGUI.PropertyGroupParent(()=>{
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_RainLayer0, "Base Layer");
                        me.ShaderProperty(_RainLayer1, "Layer 1 (R)");
                        me.ShaderProperty(_RainLayer2, "Layer 2 (G)");
                        me.ShaderProperty(_RainLayer3, "Layer 3 (B)");
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_RainStrength, "Strength");
                        me.ShaderProperty(_RainScale, "Scale");
                        me.ShaderProperty(_RainSpeed, "Speed");
                        me.ShaderProperty(_RainThreshold, "Angle Mask");
                        me.ShaderProperty(_RainThresholdSize, "Angle Mask Blend");
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_RainDensity, "Ripple Density");
                        me.ShaderProperty(_RainSize, "Ripple Size");
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_RainPuddleDensity, "Puddle Ripple Density");
                        me.ShaderProperty(_RainPuddleSize, "Puddle Ripple Size");
                    });
                });
                MGUI.ToggleGroupEnd();
            }
        }

        void DoLTCGI(Material mat){
            if (Shader.Find("LTCGI/Blur Prefilter") != null){
                LTCGILayout(mat);
            }
            else {
                _LTCGI.floatValue = 0;
                mat.SetInt("_LTCGI", 0);
                mat.DisableKeyword("LTCGI");
            }
        }

        void LTCGILayout(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _LTCGI, "LTCGI", Foldouts.Style.ThinLongToggle)){
                MGUI.PropertyGroupParent(()=>{
                    MGUI.ToggleGroup(_LTCGI.floatValue == 0);
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_LTCGIStrength, "Strength");
                        me.ShaderProperty(_LTCGIRoughness, "Roughness Multiplier");
                    });
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_LTCGI_DiffuseColor, "Diffuse Color");
                        me.ShaderProperty(_LTCGI_SpecularColor, "Specular Color");
                    });
                    MGUI.ToggleGroupEnd();
                });
            }
        }

        void DoAreaLit(Material mat){
            if (Shader.Find("AreaLit/Standard") != null){
                AreaLitLayout(mat);
            }
            else {
                _AreaLitToggle.floatValue = 0f;
                mat.SetInt("_AreaLitToggle", 0);
                mat.DisableKeyword("_AREALIT_ON");
            }
        }

        void CheckTrilinear(Texture tex) {
            if(!tex)
                return;
            if(tex.mipmapCount <= 1) {
                me.HelpBoxWithButton(
                    EditorGUIUtility.TrTextContent("Mip maps are required, please enable them in the texture import settings."),
                    EditorGUIUtility.TrTextContent("OK"));
                return;
            }
            if(tex.filterMode != FilterMode.Trilinear) {
                if(me.HelpBoxWithButton(
                    EditorGUIUtility.TrTextContent("Trilinear filtering is required, and aniso is recommended."),
                    EditorGUIUtility.TrTextContent("Fix Now"))) {
                    tex.filterMode = FilterMode.Trilinear;
                    tex.anisoLevel = 1;
                    EditorUtility.SetDirty(tex);
                }
                return;
            }
        }

        void AreaLitLayout(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _AreaLitToggle, "AreaLit", Foldouts.Style.ThinLongToggle)){
                MGUI.ToggleGroup(_AreaLitToggle.floatValue == 0);
                MGUI.PropertyGroupParent(()=>{
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_AreaLitStrength, "Strength");
                        me.ShaderProperty(_AreaLitRoughnessMult, "Roughness Multiplier");
                        me.ShaderProperty(_OpaqueLights, Tips.opaqueLightsText);
                        me.ShaderProperty(_AreaLitSpecularOcclusion, "Apply Specular Occlusion");
                    });
                    MGUI.PropertyGroup(()=>{
                        var lightMeshText = !_LightMesh.textureValue ? Tips.lightMeshText : new GUIContent(
                            Tips.lightMeshText.text + $" (max: {_LightMesh.textureValue.height})", Tips.lightMeshText.tooltip
                        );
                        me.TexturePropertySingleLine(lightMeshText, _LightMesh);
                        me.TexturePropertySingleLine(Tips.lightTex0Text, _LightTex0);
                        CheckTrilinear(_LightTex0.textureValue);
                        me.TexturePropertySingleLine(Tips.lightTex1Text, _LightTex1);
                        CheckTrilinear(_LightTex1.textureValue);
                        me.TexturePropertySingleLine(Tips.lightTex2Text, _LightTex2);
                        CheckTrilinear(_LightTex2.textureValue);
                        me.TexturePropertySingleLine(Tips.lightTex3Text, _LightTex3);
                        CheckTrilinear(_LightTex3.textureValue);
                        me.TexturePropertySingleLine(new GUIContent("Occlusion"), _AreaLitOcclusion);
                        if (_AreaLitOcclusion.textureValue){
                            me.ShaderProperty(_AreaLitOcclusionUVSet, "UV Set");
                        }
                        MGUI.TextureSO(me, _AreaLitOcclusion, _AreaLitOcclusion.textureValue);
                    });
                    MGUI.DisplayInfo("Note that the AreaLit package files MUST be inside a folder named AreaLit (case sensitive) directly in the Assets folder (Assets/AreaLit)");
                    MGUI.ToggleGroupEnd();
                });
            }
        }

        void DoLightmapSettings(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, "Lightmap Settings", Foldouts.Style.ThinLong)) {
                MGUI.PropertyGroupParent(() => {
                    MGUI.PropertyGroup(() => {
                        me.ShaderProperty(_BakeryMode, Tips.bakeryMode);
                        MGUI.ToggleFloat(me, "Bakery Specular Highlights", _BAKERY_LMSPEC, _BakeryLMSpecStrength);
                        me.ShaderProperty(_BAKERY_SHNONLINEAR, "Non-Linear SH");
                        me.ShaderProperty(_BicubicSampling, Tips.bicubicLightmap);
                        me.ShaderProperty(_IgnoreRealtimeGI, Tips.ignoreRealtimeGIText);
                        me.ShaderProperty(_AdditiveLightVolumesToggle, "Additive Light Volumes");
                    });
                });
            }
        }

        void DoDebug(Material mat){
            if (Foldouts.DoFoldout(foldouts, mat, me, _DebugMode, "Debug", Foldouts.Style.ThinLongToggle)){
                MGUI.ToggleGroup(_DebugMode.floatValue == 0);
                MGUI.PropertyGroupParent(()=>{
                    MGUI.PropertyGroup(()=>{
                        me.ShaderProperty(_DebugVertexColors, "Layer Blending");
                        me.ShaderProperty(_DebugBaseColor, "Base Color");
                        me.ShaderProperty(_DebugNormals, "Normals");
                        me.ShaderProperty(_DebugRoughness, "Roughness");
                        me.ShaderProperty(_DebugMetallic, "Metallic");
                        me.ShaderProperty(_DebugOcclusion, "Occlusion");
                        me.ShaderProperty(_DebugHeight, "Height");
                        me.ShaderProperty(_DebugAtten, "Realtime Shadows");
                        me.ShaderProperty(_DebugReflections, "Reflections");
                        me.ShaderProperty(_DebugSpecular, "Specular Highlights");
                        me.ShaderProperty(_DebugRainThreshold, "Rain Angle Mask");
                        // me.ShaderProperty(_DebugPuddleThreshold, "Puddle Angle Mask");
                    });
                });
                MGUI.ToggleGroupEnd();
            }
        }

        void SetKeywords(Material mat){
            MGUI.SetKeyword(mat, "_STOCHASTIC_0_ON", mat.GetInt("_ToggleStochastic0") == 1);
            MGUI.SetKeyword(mat, "_PARALLAX_0_ON", mat.GetInt("_ToggleParallax0") == 1);
            MGUI.SetKeyword(mat, "_POM_0_ON", mat.GetInt("_TogglePOM0") == 1);
            MGUI.SetKeyword(mat, "_LAYER_1_ON", mat.GetInt("_ToggleLayer1") == 1);
            MGUI.SetKeyword(mat, "_STOCHASTIC_1_ON", mat.GetInt("_ToggleStochastic1") == 1);
            MGUI.SetKeyword(mat, "_PARALLAX_1_ON", mat.GetInt("_ToggleParallax1") == 1);
            MGUI.SetKeyword(mat, "_POM_1_ON", mat.GetInt("_TogglePOM1") == 1);
            MGUI.SetKeyword(mat, "_LAYER_2_ON", mat.GetInt("_ToggleLayer2") == 1);
            MGUI.SetKeyword(mat, "_STOCHASTIC_2_ON", mat.GetInt("_ToggleStochastic2") == 1);
            MGUI.SetKeyword(mat, "_PARALLAX_2_ON", mat.GetInt("_ToggleParallax2") == 1);
            MGUI.SetKeyword(mat, "_POM_2_ON", mat.GetInt("_TogglePOM2") == 1);
            MGUI.SetKeyword(mat, "_LAYER_3_ON", mat.GetInt("_ToggleLayer3") == 1);
            MGUI.SetKeyword(mat, "_STOCHASTIC_3_ON", mat.GetInt("_ToggleStochastic3") == 1);
            MGUI.SetKeyword(mat, "_PARALLAX_3_ON", mat.GetInt("_ToggleParallax3") == 1);
            MGUI.SetKeyword(mat, "_POM_3_ON", mat.GetInt("_TogglePOM3") == 1);
            MGUI.SetKeyword(mat, "_REFLECTIONS_ON", mat.GetInt("_ReflectionsToggle") == 1);
            MGUI.SetKeyword(mat, "_SPECULAR_HIGHLIGHTS_ON", mat.GetInt("_SpecularHighlightsToggle") == 1);
            MGUI.SetKeyword(mat, "BAKERY_SH", mat.GetInt("_BakeryMode") == 1);
            MGUI.SetKeyword(mat, "BAKERY_RNM", mat.GetInt("_BakeryMode") == 2);
            MGUI.SetKeyword(mat, "BAKERY_MONOSH", mat.GetInt("_BakeryMode") == 3);
            MGUI.SetKeyword(mat, "BAKERY_LMSPEC", mat.GetInt("_BAKERY_LMSPEC") == 1);
            MGUI.SetKeyword(mat, "BAKERY_SHNONLINEAR", mat.GetInt("_BAKERY_SHNONLINEAR") == 1);
            MGUI.SetKeyword(mat, "_HEIGHT_BLENDING_ON", mat.GetInt("_HeightBlend1") == 1 || mat.GetInt("_HeightBlend2") == 1 || mat.GetInt("_HeightBlend3") == 1);
            MGUI.SetKeyword(mat, "_SSR_ON", mat.GetInt("_SSRToggle") == 1);
            MGUI.SetKeyword(mat, "LTCGI", mat.GetInt("_LTCGI") == 1);
            MGUI.SetKeyword(mat, "_RAIN_ON", mat.GetInt("_RainToggle") == 1);
            MGUI.SetKeyword(mat, "_AREALIT_ON", mat.GetInt("_AreaLitToggle") == 1);
            MGUI.SetKeyword(mat, "_DEBUGMODE_ON", mat.GetInt("_DebugMode") == 1);
        }

        void ApplySSRRenderSettings(Material mat){
            if (_SSRToggle.floatValue == 1){
                mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                mat.SetShaderPassEnabled("Always", true);
                
            }
            else {
                mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry;
                mat.SetShaderPassEnabled("Always", false);

            }
        }

        public override void AssignNewShaderToMaterial(Material mat, Shader oldShader, Shader newShader) {
            base.AssignNewShaderToMaterial(mat, oldShader, newShader);
            MGUI.ClearKeywords(mat);
            SetKeywords(mat);
            ApplySSRRenderSettings(mat);
        }
    }
}