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

namespace Mochie {
        
    public class GlassEditor : ShaderGUI {

        public static Dictionary<Material, Toggles> foldouts = new Dictionary<Material, Toggles>();
        Toggles toggles = new Toggles(new string[] {
            "Surface",
            "Specularity",
            "Emission",
            "Rain",
            "Lightmap Settings",
            "LTCGI",
            "AreaLit",
            "Render Settings"
        }, 1);

        string versionLabel = "v1.11.1";

        // Surface
        MaterialProperty _GrabpassTint = null;
        MaterialProperty _SpecularityTint = null;
        MaterialProperty _BaseColorTint = null;
        MaterialProperty _MainTex = null;
        MaterialProperty _RoughnessMap = null;
        MaterialProperty _MetallicMap = null;
        MaterialProperty _OcclusionMap = null;
        MaterialProperty _NormalMap = null;
        MaterialProperty _Roughness = null;
        MaterialProperty _Metallic = null;
        MaterialProperty _Occlusion = null;
        MaterialProperty _NormalStrength = null;
        MaterialProperty _Refraction = null;
        MaterialProperty _Blur = null;
        MaterialProperty _BlurQuality = null;
        MaterialProperty _RefractionIOR = null;
        MaterialProperty _RefractVertexNormal = null;

        // Emission
        MaterialProperty _EmissionMap = null;
        MaterialProperty _EmissionColor= null;
        MaterialProperty _EmissionStrength = null;
        MaterialProperty _EmissionParallaxDepth = null;

        // Rain
        // MaterialProperty _RainToggle = null;
        MaterialProperty _Speed = null;
        MaterialProperty _XScale = null;
        MaterialProperty _YScale = null;
        MaterialProperty _Strength = null;
        MaterialProperty _RainMode = null;
        MaterialProperty _RainMask = null;
        MaterialProperty _RainMaskChannel = null;
        MaterialProperty _RippleScale = null;
        MaterialProperty _RippleSpeed = null;
        MaterialProperty _RippleStrength = null;
        MaterialProperty _RippleSize = null;
        MaterialProperty _RippleDensity = null;
        MaterialProperty _DynamicDroplets = null;
        // MaterialProperty _RainBias = null;
        MaterialProperty _RainThreshold = null;
        MaterialProperty _RainThresholdSize = null;
        
        // Lightmap Settings
        MaterialProperty _LightmapDistortion = null;
        MaterialProperty _IndirectStrength = null;
        MaterialProperty _IndirectSaturation = null;
        MaterialProperty _BAKERY_LMSPEC = null;
        MaterialProperty _BakeryLMSpecStrength = null;
       //  MaterialProperty _BAKERY_SHNONLINEAR = null;
        MaterialProperty _BakeryMode = null;
        MaterialProperty _BicubicSampling = null;
        MaterialProperty _IgnoreRealtimeGI = null;

        // Render Settings
        MaterialProperty _ReflectionsToggle = null;
        MaterialProperty _ReflectionStrength = null;
        MaterialProperty _SpecularToggle = null;
        MaterialProperty _SpecularStrength = null;
        MaterialProperty _SSRToggle = null;
        MaterialProperty _SSRStrength = null;
        MaterialProperty _SSREdgeFade = null;
        MaterialProperty _SSRHeight = null;
        MaterialProperty _GSAAToggle = null;
        MaterialProperty _GSAAStrength = null;
        MaterialProperty _Culling = null;
        MaterialProperty _SamplingMode = null;
        MaterialProperty _BlendMode = null;
        MaterialProperty _LitBaseColor = null;
        MaterialProperty _QueueOffset = null;
        MaterialProperty _TexCoordSpace = null;
        MaterialProperty _TexCoordSpaceSwizzle = null;
        MaterialProperty _GlobalTexCoordScale = null;

        // Area Lit
        MaterialProperty _AreaLitToggle = null;
        MaterialProperty _AreaLitMask = null;
        MaterialProperty _AreaLitStrength = null;
        MaterialProperty _AreaLitRoughnessMult = null;
        MaterialProperty _LightMesh = null;
        MaterialProperty _LightTex0 = null;
        MaterialProperty _LightTex1 = null;
        MaterialProperty _LightTex2 = null;
        MaterialProperty _LightTex3 = null;
        MaterialProperty _OpaqueLights = null;

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

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

        public override void OnGUI(MaterialEditor me, MaterialProperty[] props) {
            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;
            bool isTwoPass = mat.shader.name.Contains("(Two Pass)"); 

            if (!foldouts.ContainsKey(mat))
                foldouts.Add(mat, toggles);

            if (mat.GetInt("_MaterialResetCheck") == 0){
                mat.SetInt("_MaterialResetCheck", 1);
                SetKeywords(mat);
                SetBlendMode(mat);
            }

            MGUI.DoHeader(isTwoPass ? "TWO PASS GLASS" : "GLASS");

            EditorGUI.BeginChangeCheck(); {
                
                // Surface
                if (Foldouts.DoFoldout(foldouts, mat, "Surface", Foldouts.Style.Standard)) {
                    MGUI.PropertyGroupParent(()=>{
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_BlendMode, "Transparency");
                            if (_BlendMode.floatValue == 0)
                                me.ShaderProperty(_GrabpassTint, "Grabpass Tint");
                            me.ShaderProperty(_SpecularityTint, "Specularity Tint");
                            me.ShaderProperty(_BaseColorTint, "Base Color Tint");
                        });
                        MGUI.PropertyGroup(()=>{
                            me.TexturePropertySingleLine(Tips.baseColorLabel, _MainTex, _LitBaseColor);
                            MGUI.TexPropLabel(Tips.litBaseColorText, 100, false);
                            MGUI.TextureSO(me, _MainTex, _MainTex.textureValue);
                            me.TexturePropertySingleLine(Tips.metallicText, _MetallicMap, _Metallic);
                            MGUI.sRGBWarning(_MetallicMap);
                            MGUI.TextureSO(me, _MetallicMap, _MetallicMap.textureValue);
                            me.TexturePropertySingleLine(Tips.roughnessTexLabel, _RoughnessMap, _Roughness);
                            MGUI.sRGBWarning(_RoughnessMap);
                            MGUI.TextureSO(me, _RoughnessMap, _RoughnessMap.textureValue);
                            me.TexturePropertySingleLine(Tips.occlusionTexLabel, _OcclusionMap, _OcclusionMap.textureValue ? _Occlusion : null);
                            MGUI.sRGBWarning(_OcclusionMap);
                            MGUI.TextureSO(me, _OcclusionMap, _OcclusionMap.textureValue);
                            me.TexturePropertySingleLine(Tips.normalMapText, _NormalMap, _NormalMap.textureValue ? _NormalStrength : null);
                            MGUI.TextureSO(me, _NormalMap, _NormalMap.textureValue);
                        });
                        if (_BlendMode.floatValue == 0){
                            MGUI.PropertyGroup(()=>{
                                me.ShaderProperty(_BlurQuality, "Blur Quality");
                                me.ShaderProperty(_Blur, "Blur Strength");
                                me.ShaderProperty(_Refraction, Tips.normalRefractionText);
                                if (!isTwoPass)
                                    MGUI.ToggleFloat(me, Tips.meshRefractionText, _RefractVertexNormal, _RefractionIOR);
                            });
                        }
                    });
                }

                // Specularity
                if (Foldouts.DoFoldout(foldouts, mat, "Specularity", Foldouts.Style.Standard)) {
                    MGUI.PropertyGroupParent(()=>{
                        MGUI.PropertyGroup(()=>{
                            MGUI.ToggleFloat(me, Tips.cubemapReflectionsText, _ReflectionsToggle, _ReflectionStrength);
                            if (!isTwoPass){
                                MGUI.ToggleFloat(me, Tips.ssrText, _SSRToggle, _SSRStrength);
                                if (_SSRToggle.floatValue == 1){
                                    me.ShaderProperty(_SSREdgeFade, Tips.ssrEdgeFadeText);
                                    me.ShaderProperty(_SSRHeight, Tips.ssrDepthText);
                                }
                            }
                            MGUI.ToggleFloat(me, Tips.specularHighlightsText, _SpecularToggle, _SpecularStrength);
                            MGUI.ToggleFloat(me, Tips.gsaa, _GSAAToggle, _GSAAStrength);
                        });
                    });
                }
                
                // Emission
                if (Foldouts.DoFoldout(foldouts, mat, "Emission", Foldouts.Style.Standard)) {
                    MGUI.PropertyGroupParent(()=>{
                        MGUI.ToggleGroup(!me.EmissionEnabledProperty());
                        me.LightmapEmissionFlagsProperty(0, true);
                        MGUI.Space4();
                        MGUI.PropertyGroup(()=>{
                            me.TexturePropertySingleLine(Tips.emissionText, _EmissionMap, _EmissionColor);
                            MGUI.TextureSO(me, _EmissionMap, _EmissionMap.textureValue);
                            me.ShaderProperty(_EmissionStrength, "Strength");
                            if (!isTwoPass)
                                me.ShaderProperty(_EmissionParallaxDepth, "Parallax Offset");
                        });
                        if ((_BlendMode.floatValue == 1 || _BlendMode.floatValue == 2) && _EmissionParallaxDepth.floatValue > 0){
                            MGUI.PropertyGroup(()=>{
                                me.ShaderProperty(_BlurQuality, "Blur Quality");
                                me.ShaderProperty(_Blur, "Blur Strength");
                            });
                        }
                         MGUI.ToggleGroupEnd();
                    });
                }

                // Rain
                if (Foldouts.DoFoldout(foldouts, mat, me, _RainMode, "Rain", Foldouts.Style.StandardToggle)) {
                    MGUI.PropertyGroupParent(()=>{
                        if (_RainMode.floatValue == 0 || _RainMode.floatValue == 1){
                            RainDroplets(me);
                        }
                        else if (_RainMode.floatValue == 2){
                            RainRipples(me);
                        }
                        else if (_RainMode.floatValue == 3){
                            MGUI.BoldLabel("Droplets");
                            RainDroplets(me);
                            MGUI.BoldLabel("Ripples");
                            RainRipples(me);
                            MGUI.BoldLabel("Both");
                            RainBoth(me);
                        }
                    });
                }

                // Lightmap Settings
                if (Foldouts.DoFoldout(foldouts, mat, "Lightmap Settings", Foldouts.Style.Standard)) {
                    MGUI.PropertyGroupParent(()=>{
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_BakeryMode, Tips.bakeryMode);
                            MGUI.ToggleFloat(me, "Bakery Specular Highlights", _BAKERY_LMSPEC, _BakeryLMSpecStrength);
                        });
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_LightmapDistortion, "Lightmap UV Distortion");
                            me.ShaderProperty(_IndirectStrength, "Lightmap Strength");
                            me.ShaderProperty(_IndirectSaturation, "Lightmap Saturation");
                        });
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_BicubicSampling, Tips.bicubicLightmap);
                            me.ShaderProperty(_IgnoreRealtimeGI, Tips.ignoreRealtimeGIText);
                        });
                    });
                }

                // LTCGI
                if (Shader.Find("LTCGI/Blur Prefilter") != null){
                    if (Foldouts.DoFoldout(foldouts, mat, me, _LTCGI, "LTCGI", Foldouts.Style.StandardToggle)) {
                        MGUI.PropertyGroupParent(()=>{
                            MGUI.ToggleGroup(_LTCGI.floatValue == 0);
                            MGUI.PropertyGroup(()=>{
                                me.ShaderProperty(_LTCGIStrength, "Strength");
                                me.ShaderProperty(_LTCGIRoughness, "Roughness Multiplier");
                                me.ShaderProperty(_LTCGI_SpecularColor, "Tint");
                            });
                            MGUI.ToggleGroupEnd();
                        });
                    }
                }
                else {
                    _LTCGI.floatValue = 0;
                    mat.SetInt("_LTCGI", 0);
                    mat.DisableKeyword("LTCGI");
                }

                // AreaLit
                if (Shader.Find("AreaLit/Standard") != null){
                    if (Foldouts.DoFoldout(foldouts, mat, me, _AreaLitToggle, "AreaLit", Foldouts.Style.StandardToggle)) {
                        MGUI.PropertyGroupParent(()=>{
                            MGUI.ToggleGroup(_AreaLitToggle.floatValue == 0);
                            MGUI.PropertyGroup(()=>{
                                me.TexturePropertySingleLine(Tips.maskText, _AreaLitMask);
                                MGUI.TextureSO(me, _AreaLitMask, _AreaLitMask.textureValue);
                                me.ShaderProperty(_AreaLitStrength, "Strength");
                                me.ShaderProperty(_AreaLitRoughnessMult, "Roughness Multiplier");
                                me.ShaderProperty(_OpaqueLights, Tips.opaqueLightsText);
                            });
                            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);
                                MGUI.CheckTrilinear(_LightTex0.textureValue, me);
                                me.TexturePropertySingleLine(Tips.lightTex1Text, _LightTex1);
                                MGUI.CheckTrilinear(_LightTex1.textureValue, me);
                                me.TexturePropertySingleLine(Tips.lightTex2Text, _LightTex2);
                                MGUI.CheckTrilinear(_LightTex2.textureValue, me);
                                me.TexturePropertySingleLine(Tips.lightTex3Text, _LightTex3);
                                MGUI.CheckTrilinear(_LightTex3.textureValue, me);
                            });
                            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();
                        });
                    }
                }
                else {
                    _AreaLitToggle.floatValue = 0f;
                    mat.SetInt("_AreaLitToggle", 0);
                    mat.DisableKeyword("_AREALIT_ON");
                }

                // Render Settings
                if (Foldouts.DoFoldout(foldouts, mat, "Render Settings", Foldouts.Style.Standard)) {
                    MGUI.PropertyGroupParent(()=>{
                        MGUI.PropertyGroup(()=>{
                            me.ShaderProperty(_SamplingMode, Tips.samplingModeText);
                            me.ShaderProperty(_TexCoordSpace, Tips.texCoordSpaceText);
                            if (_TexCoordSpace.floatValue == 1f){
                                me.ShaderProperty(_TexCoordSpaceSwizzle, Tips.swizzleText);
                            }
                            me.ShaderProperty(_GlobalTexCoordScale, "Texture Coordinate Scale");
                        });
                        MGUI.PropertyGroup(()=>{
                            MGUI.SpaceN1();
                            _QueueOffset.floatValue = (int)_QueueOffset.floatValue;
                            MGUI.DummyProperty("Render Queue:", mat.renderQueue.ToString());
                            me.ShaderProperty(_QueueOffset, Tips.queueOffset);
                            if (!isTwoPass)
                                me.ShaderProperty(_Culling, "Culling Mode");
                        });
                    });
                }
            }
            if (EditorGUI.EndChangeCheck()){
                SetKeywords(mat);
            }
            SetBlendMode(mat);
            MGUI.Space10();
            MGUI.DoFooter(versionLabel);
        }

        void RainDroplets(MaterialEditor me){
            MGUI.ToggleGroup(_RainMode.floatValue == 0);
            MGUI.PropertyGroup(()=>{
                if (_RainMode.floatValue != 3){
                    me.TexturePropertySingleLine(Tips.maskText, _RainMask, _RainMaskChannel);
                    MGUI.TextureSO(me, _RainMask, _RainMask.textureValue);
                }
                me.ShaderProperty(_Strength, "Strength");
                me.ShaderProperty(_Speed, "Speed");
                me.ShaderProperty(_XScale, "X Scale");
                me.ShaderProperty(_YScale, "Y Scale");
                me.ShaderProperty(_DynamicDroplets, "Dynamic Droplets");
            });
            MGUI.ToggleGroupEnd();
        }

        void RainRipples(MaterialEditor me){
            MGUI.PropertyGroup(()=>{
                if (_RainMode.floatValue != 3){
                    me.TexturePropertySingleLine(Tips.maskText, _RainMask, _RainMaskChannel);
                    MGUI.TextureSO(me, _RainMask, _RainMask.textureValue);
                }
                me.ShaderProperty(_RippleStrength, "Strength");
                me.ShaderProperty(_RippleSpeed, "Speed");
                me.ShaderProperty(_RippleScale, "Scale");
                me.ShaderProperty(_RippleDensity, "Density");
                me.ShaderProperty(_RippleSize, "Size");
            });
        }

        void RainBoth(MaterialEditor me){
            MGUI.PropertyGroup(()=>{
                me.TexturePropertySingleLine(Tips.maskText, _RainMask, _RainMaskChannel);
                MGUI.TextureSO(me, _RainMask, _RainMask.textureValue);
                me.ShaderProperty(_RainThreshold, "Angle Threshold");
                me.ShaderProperty(_RainThresholdSize, "Threshold Blend");
            });
        }

        void SetKeywords(Material mat){
            bool isEmissive = (mat.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
            bool isTwoPass = mat.shader.name.Contains("(Two Pass)"); 
            int rainMode = mat.GetInt("_RainMode");
            int blurMode = mat.GetInt("_BlurQuality");
            MGUI.SetKeyword(mat, "_EMISSION_ON", isEmissive);
            MGUI.SetKeyword(mat, "_STOCHASTIC_SAMPLING_ON", mat.GetInt("_SamplingMode") == 1);
            MGUI.SetKeyword(mat, "_NORMALMAP_ON", mat.GetTexture("_NormalMap"));
            MGUI.SetKeyword(mat, "_RAIN_ON", rainMode > 0);
            MGUI.SetKeyword(mat, "_RAINMODE_RIPPLE", rainMode == 2);
            MGUI.SetKeyword(mat, "_RAINMODE_AUTO", rainMode == 3);
            MGUI.SetKeyword(mat, "_BLURQUALITY_LOW", blurMode == 0);
            MGUI.SetKeyword(mat, "_BLURQUALITY_MED", blurMode == 1);
            MGUI.SetKeyword(mat, "_BLURQUALITY_HIGH", blurMode == 2);
            MGUI.SetKeyword(mat, "_BLURQUALITY_ULTRA", blurMode == 3);
            MGUI.SetKeyword(mat, "_AREALIT_ON", mat.GetInt("_AreaLitToggle") == 1);
            MGUI.SetKeyword(mat, "LTCGI", mat.GetInt("_LTCGI") == 1);
            MGUI.SetKeyword(mat, "_REFLECTIONS_ON", mat.GetInt("_ReflectionsToggle") == 1);
            MGUI.SetKeyword(mat, "_SPECULAR_HIGHLIGHTS_ON", mat.GetInt("_SpecularToggle") == 1);
            MGUI.SetKeyword(mat, "_SSR_ON", mat.GetInt("_SSRToggle") == 1 && mat.GetInt("_BlendMode") == 0 && !isTwoPass);
            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);
        }

        void SetBlendMode(Material mat){
            int blendMode = mat.GetInt("_BlendMode");
            // float roughness = mat.GetFloat("_Roughness");
            // bool rainToggle = mat.GetInt("_RainMode") > 0;
            // bool hasNormal = mat.GetTexture("_NormalMap") && mat.GetFloat("_NormalStrength") > 0;
            // bool canGrab = roughness > 0 || rainToggle || hasNormal;
            // if (blendMode == 0) blendMode = canGrab ? blendMode : 1;
            #if UNITY_ANDROID
                if (blendMode == 0){
                    mat.SetInt("_BlendMode", 1);
                    blendMode = 1;
                }
            #endif

            switch (blendMode){
                case 0: // Grabpass
                    mat.SetOverrideTag("RenderType", "Transparent");
                    mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    mat.SetInt("_ZWrite", 0);
                    mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent+mat.GetInt("_QueueOffset");
                    mat.SetShaderPassEnabled("Always", true);
                    MGUI.SetKeyword(mat, "_GRABPASS_ON", true);
                    MGUI.SetKeyword(mat, "_PREMULTIPLIED_ON", false);
                    break;
                case 1: // Premultiplied
                    mat.SetOverrideTag("RenderType", "Transparent");
                    mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    mat.SetInt("_ZWrite", 0);
                    mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent+mat.GetInt("_QueueOffset");
                    mat.SetShaderPassEnabled("Always", false);
                    MGUI.SetKeyword(mat, "_GRABPASS_ON", false);
                    MGUI.SetKeyword(mat, "_PREMULTIPLIED_ON", true);
                    break;
                case 2: // Opaque
                    mat.SetOverrideTag("RenderType", "Opaque");
                    mat.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    mat.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    mat.SetInt("_ZWrite", 1);
                    mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Geometry+mat.GetInt("_QueueOffset");
                    mat.SetShaderPassEnabled("Always", false);
                    MGUI.SetKeyword(mat, "_GRABPASS_ON", false);
                    MGUI.SetKeyword(mat, "_PREMULTIPLIED_ON", false);
                    break;
                default: break;
            }
        }

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