#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

public class Fracture200GUI : ShaderGUI
{
    private Font VrchatFont = (Font)Resources.Load(@"BankGothic");

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

    private MaterialProperty UFPS = null;
    private MaterialProperty DF = null;
    private MaterialProperty _fe = null;
    private MaterialProperty _fs = null;

    private MaterialProperty _MainColor = null;
    private MaterialProperty _Alpha = null;

    private MaterialProperty BGS = null;
    private MaterialProperty _bgColor = null;
    private MaterialProperty _bgAlpha = null;

    private MaterialProperty WM = null;
    private MaterialProperty _ScreenTex = null;
    private MaterialProperty _Width = null;
    private MaterialProperty _Height = null;
    private MaterialProperty _Scale = null;
    private MaterialProperty _LR = null;
    private MaterialProperty _UD = null;
    private MaterialProperty _trot = null;

    private MaterialProperty SSG = null;
    private MaterialProperty _Columns = null;
    private MaterialProperty _Rows = null;
    private MaterialProperty _fps = null;
    private MaterialProperty _Moffset = null;

	private MaterialProperty _TexCube = null;
    private MaterialProperty _FrontTex = null;
    private MaterialProperty _BackTex = null;
    private MaterialProperty _LeftTex = null;
    private MaterialProperty _RightTex = null;
    private MaterialProperty _UpTex = null;
    private MaterialProperty _DownTex = null;
	private MaterialProperty _Exposure = null;
    private MaterialProperty _cmColor = null;

    private MaterialProperty _TotalOffset = null;
    private MaterialProperty _TotalNormalOffset = null;
    private MaterialProperty _NormalOffset = null;
    private MaterialProperty _RandomNormalMovingAmplitude = null;
    private MaterialProperty _RandomNormalMovingSpeed = null;
    private MaterialProperty _TotalRotation = null;
    private MaterialProperty _ConstantRotationSpeed = null;
    private MaterialProperty _ConstantRotation = null;
    private MaterialProperty _RandomConstantRotationSpeed = null;
    private MaterialProperty _RandomConstantRotation = null;
    private MaterialProperty _RandomRotationAmplitude = null;
    private MaterialProperty _RandomRotationSpeed = null;
    private MaterialProperty _RandomSeed = null;
    
    private MaterialProperty _WireColor = null;
    private MaterialProperty _WireColorPower = null;
    private MaterialProperty _WireGlow = null;
    private MaterialProperty _WireGlowPower = null;
    private MaterialProperty _WireWitdh = null;
    private MaterialProperty _WireAlpha = null;

    private MaterialProperty _Stencil = null;
    private MaterialProperty _ReadMask = null;
    private MaterialProperty _WriteMask = null;
    private MaterialProperty _StencilComp = null;
    private MaterialProperty _StencilOp = null;
    private MaterialProperty _StencilFail = null;
    private MaterialProperty _StencilZFail = null;

    private MaterialProperty _Queue = null;
    private MaterialProperty _UseCustomQueue = null;

    bool m_FirstTimeApply = true;
    List<ParticleSystemRenderer> m_RenderersUsingThisMaterial = new List<ParticleSystemRenderer>();
    Mesh CorrectMesh = null;

    public override void OnGUI(MaterialEditor editor, MaterialProperty[] properties)
    {
        Material material; Shader shader; PrepareGUI(editor, properties, out material, out shader);

        DrawBanner(shader.name.Split('/').Last(), "Open Discord", "https://discord.gg/E3HPmNR");

        EditorGUILayout.BeginVertical("GroupBox");

        MakeFoldout(material, "_RendererGUI", "Renderer");
        if (material.GetInt("_RendererGUI") == 1)
        {
            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(UFPS, MakeLabel(UFPS));
            if (material.GetInt("UFPS") == 1 && m_FirstTimeApply)
            {
                CacheRenderersUsingThisMaterial(material);
                string CorrectMeshPath = AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets("FractureSphereMesh")[0]);
                CorrectMesh = AssetDatabase.LoadAssetAtPath(CorrectMeshPath, typeof(Mesh)) as Mesh;
                m_FirstTimeApply = false;
            }
            EditorGUILayout.EndVertical();

            EditorGUI.BeginDisabledGroup(material.GetInt("UFPS") == 0);
            EditorGUILayout.BeginVertical(GUI.skin.box);
            DoConfigureRendererArea(material);
            EditorGUILayout.EndVertical();
            EditorGUI.EndDisabledGroup();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(DF, MakeLabel(DF));
            EditorGUI.BeginDisabledGroup(material.GetInt("DF") == 2);
            editor.ShaderProperty(_fs, MakeLabel(_fs));
            editor.ShaderProperty(_fe, MakeLabel(_fe));
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndVertical();
        }

        MakeFoldout(material, "_FractureGUI", "Fracture");
        if (material.GetInt("_FractureGUI") == 1)
        {
            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_MainColor, MakeLabel(_MainColor));
            editor.ShaderProperty(_Alpha, MakeLabel(_Alpha));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_TotalOffset, MakeLabel(_TotalOffset));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_TotalNormalOffset, MakeLabel(_TotalNormalOffset));
            editor.ShaderProperty(_NormalOffset, MakeLabel(_NormalOffset));
            editor.ShaderProperty(_RandomNormalMovingAmplitude, MakeLabel(_RandomNormalMovingAmplitude));
            editor.ShaderProperty(_RandomNormalMovingSpeed, MakeLabel(_RandomNormalMovingSpeed));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_TotalRotation, MakeLabel(_TotalRotation));
            editor.ShaderProperty(_ConstantRotationSpeed, MakeLabel(_ConstantRotationSpeed));
            editor.ShaderProperty(_ConstantRotation, MakeLabel(_ConstantRotation));
            editor.ShaderProperty(_RandomConstantRotationSpeed, MakeLabel(_RandomConstantRotationSpeed));
            editor.ShaderProperty(_RandomConstantRotation, MakeLabel(_RandomConstantRotation));
            editor.ShaderProperty(_RandomRotationAmplitude, MakeLabel(_RandomRotationAmplitude));
            editor.ShaderProperty(_RandomRotationSpeed, MakeLabel(_RandomRotationSpeed));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_RandomSeed, MakeLabel(_RandomSeed));
            EditorGUILayout.EndVertical();
        }

        MakeFoldout(material, "_WireGUI", "Wireframe");
        if (material.GetInt("_WireGUI") == 1)
        {
            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_WireColor, MakeLabel(_WireColor));
            editor.ShaderProperty(_WireColorPower, MakeLabel(_WireColorPower));
            editor.ShaderProperty(_WireAlpha, MakeLabel(_WireAlpha));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_WireGlow, MakeLabel(_WireGlow));
            editor.ShaderProperty(_WireGlowPower, MakeLabel(_WireGlowPower));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_WireWitdh, MakeLabel(_WireWitdh));
            EditorGUILayout.EndVertical();
        }

        MakeFoldout(material, "_BackgroundGUI", "Background");
        if (material.GetInt("_BackgroundGUI") == 1)
        {
            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(BGS, MakeLabel(BGS));
            EditorGUILayout.EndVertical();
            int mode = material.GetInt("BGS");
            EditorGUILayout.BeginVertical(GUI.skin.box);
            if (mode == 0)
            {
                editor.ShaderProperty(_bgColor, MakeLabel(_bgColor));
                editor.ShaderProperty(_bgAlpha, MakeLabel(_bgAlpha));
            }
            else if (mode == 2)
            {
                DrawTexture(editor, _ScreenTex);
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical(GUI.skin.box);
                editor.ShaderProperty(_bgColor, MakeLabel(_bgColor));
                editor.ShaderProperty(_bgAlpha, MakeLabel(_bgAlpha));
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical(GUI.skin.box);
                editor.ShaderProperty(WM, MakeLabel(WM));
                editor.ShaderProperty(_Width, MakeLabel(_Width));
                editor.ShaderProperty(_Height, MakeLabel(_Height));
                editor.ShaderProperty(_Scale, MakeLabel(_Scale));
                editor.ShaderProperty(_LR, MakeLabel(_LR));
                editor.ShaderProperty(_UD, MakeLabel(_UD));
                editor.ShaderProperty(_trot, MakeLabel(_trot));
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical(GUI.skin.box);
                editor.ShaderProperty(SSG, MakeLabel(SSG));
                if (material.GetInt("SSG") == 1)
                {
                    editor.ShaderProperty(_Columns, MakeLabel(_Columns));
                    editor.ShaderProperty(_Rows, MakeLabel(_Rows));
                    editor.ShaderProperty(_fps, MakeLabel(_fps));
                    editor.ShaderProperty(_Moffset, MakeLabel(_Moffset));
                }
            }
            else if (mode == 1 || mode == 3)
            {
                if (mode == 3)
                {
                    DrawTexture(editor, _FrontTex);
                    DrawTexture(editor, _BackTex);
                    DrawTexture(editor, _LeftTex);
                    DrawTexture(editor, _RightTex);
                    DrawTexture(editor, _UpTex);
                    DrawTexture(editor, _DownTex);
                }
                else
                {
                    DrawTexture(editor, _TexCube);
                }
                EditorGUILayout.EndVertical();

                EditorGUILayout.BeginVertical(GUI.skin.box);
                editor.ShaderProperty(_cmColor, MakeLabel(_cmColor));
                editor.ShaderProperty(_bgAlpha, MakeLabel(_bgAlpha));
                editor.ShaderProperty(_Exposure, MakeLabel(_Exposure));
            }
            EditorGUILayout.EndVertical();
        }

        MakeFoldout(material, "_AdvancedGUI", "Advanced");
        if (material.GetInt("_AdvancedGUI") == 1)
        {
            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_Stencil, MakeLabel(_Stencil));
            editor.ShaderProperty(_ReadMask, MakeLabel(_ReadMask));
            editor.ShaderProperty(_WriteMask, MakeLabel(_WriteMask));
            editor.ShaderProperty(_StencilComp, MakeLabel(_StencilComp));
            editor.ShaderProperty(_StencilOp, MakeLabel(_StencilOp));
            editor.ShaderProperty(_StencilFail , MakeLabel(_StencilFail ));
            editor.ShaderProperty(_StencilZFail, MakeLabel(_StencilZFail));
            EditorGUILayout.EndVertical();

            EditorGUILayout.BeginVertical(GUI.skin.box);
            editor.ShaderProperty(_UseCustomQueue, MakeLabel(_UseCustomQueue));
            EditorGUI.BeginDisabledGroup(material.GetInt("_UseCustomQueue") == 0);
            editor.ShaderProperty(_Queue, MakeLabel(_Queue));
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndVertical();
        }

        EditorGUILayout.EndVertical();

        DrawBanner("Patreon", "Open Patreon", "https://www.patreon.com/dopestuff");

        DrawCredits();
    }

    private void PrepareGUI(MaterialEditor editor, MaterialProperty[] properties, out Material material, out Shader shader)
    {
        material = editor.target as Material;
        shader = material.shader;

        //Thanks Xiexe
        foreach (var property in GetType().GetFields(bindingFlags))
        {
            if (property.FieldType == typeof(MaterialProperty))
            {
                try { property.SetValue(this, FindProperty(property.Name, properties)); } catch { }
            }
        }
        //

        if (material.GetInt("_UseCustomQueue") == 1)
        {
            material.renderQueue = Mathf.Clamp(material.GetInt("_Queue"), 0, int.MaxValue); //2147483647
        }
        else
        {
            material.renderQueue = -1;
            material.SetInt("_Queue", 4000);
        }
    }

    private void DrawTexture(MaterialEditor editor, MaterialProperty tex, MaterialProperty prop = null)
    {
        editor.TexturePropertySingleLine(MakeLabel(tex), tex, prop);
    }

    private void DrawBanner(string MainText, string OtherText, string URL)
    {
        GUIStyle OtherStyle = new GUIStyle { font = VrchatFont, alignment = TextAnchor.LowerRight, fontSize = 12, padding = new RectOffset(0, 1, 0, 1) };
        OtherStyle.normal.textColor = new Color(0.9f, 0.9f, 0.9f);

        GUIStyle MainStyle = new GUIStyle { font = VrchatFont, alignment = TextAnchor.MiddleCenter, fontSize = 18, padding = new RectOffset(0, 1, 0, 1) };
        MainStyle.normal.textColor = new Color(1f, 1f, 1f);

        EditorGUILayout.BeginVertical("GroupBox");
        Rect BannerRect = GUILayoutUtility.GetRect(0, int.MaxValue, 35, 35);
        EditorGUI.DrawRect(BannerRect, new Color(0f, 0f, 0f));
        EditorGUI.LabelField(BannerRect, OtherText, OtherStyle);
        EditorGUI.LabelField(BannerRect, MainText, MainStyle);

        if (GUI.Button(BannerRect, string.Empty, new GUIStyle()))
        {
            Application.OpenURL(URL);
        }

        EditorGUILayout.EndVertical();
    }

    private static GUIContent MakeLabel(MaterialProperty property, string tooltip = null)
    {
        GUIContent staticLabel = new GUIContent { text = property.displayName, tooltip = tooltip };
        return staticLabel;
    }

    private void DrawCredits()
    {
        EditorGUILayout.BeginVertical("GroupBox");

        GUIStyle TextStyle = new GUIStyle { font = VrchatFont, fontSize = 15, fontStyle = FontStyle.Italic };
        GUILayout.Label("Shader by:", TextStyle);
        GUILayout.Space(2);
        GUILayout.Label("Doppelgänger#8376", TextStyle);
        GUILayout.Space(6);
        GUILayout.Label("Thanks for help with editor:", TextStyle);
        GUILayout.Space(2);
        GUILayout.Label("Bkp#8336", TextStyle);

        EditorGUILayout.EndVertical();
    }

    private void MakeFoldout(Material material, string foldout, string name) //Foldout
    {
        EditorGUILayout.BeginHorizontal("Button");
        GUILayout.Space(10);
        material.SetInt(foldout, EditorGUILayout.Foldout(material.GetInt(foldout) > 0, name, true) ? 1 : 0);
        EditorGUILayout.EndHorizontal();
    }

    void CacheRenderersUsingThisMaterial(Material material)
    {
        m_RenderersUsingThisMaterial.Clear();
        ParticleSystemRenderer[] renderers = Resources.FindObjectsOfTypeAll(typeof(ParticleSystemRenderer)) as ParticleSystemRenderer[];
        foreach (ParticleSystemRenderer renderer in renderers)
        {
            var go = renderer.gameObject;
            if (go.hideFlags == HideFlags.NotEditable || go.hideFlags == HideFlags.HideAndDontSave)
                continue;
            if (renderer.sharedMaterial == material)
                m_RenderersUsingThisMaterial.Add(renderer);
        }
    }

    void DoConfigureRendererArea(Material material)
    {
        List<ParticleSystemVertexStream> streams = new List<ParticleSystemVertexStream>();
        
        streams.Add(ParticleSystemVertexStream.Position);
        streams.Add(ParticleSystemVertexStream.Center);
        streams.Add(ParticleSystemVertexStream.SizeX);

        if (GUILayout.Button(EditorGUIUtility.TrTextContent("Configure Particle Systems Renderer"), EditorStyles.miniButton))
        {
            Undo.RecordObjects(m_RenderersUsingThisMaterial.Where(r => r != null).ToArray(), L10n.Tr("Configure Particle Systems Renderer from material"));
            foreach (ParticleSystemRenderer renderer in m_RenderersUsingThisMaterial)
            {
                if (renderer != null)
                {
                    renderer.SetActiveVertexStreams(streams);
                    renderer.renderMode = ParticleSystemRenderMode.Mesh;
                    renderer.mesh = CorrectMesh;
                    renderer.alignment = ParticleSystemRenderSpace.World;
                }
            }
        }

        string Errors = string.Empty;
        List<ParticleSystemVertexStream> rendererStreams = new List<ParticleSystemVertexStream>();
        foreach (ParticleSystemRenderer renderer in m_RenderersUsingThisMaterial)
        {
            if (renderer != null)
            {
                renderer.GetActiveVertexStreams(rendererStreams);
                bool streamsValid = rendererStreams.SequenceEqual(streams);
                bool rendererModeValid = renderer.renderMode == ParticleSystemRenderMode.Mesh;
                bool meshValid = renderer.mesh == CorrectMesh;
                bool alignmentValid = renderer.alignment == ParticleSystemRenderSpace.World;
                if (!streamsValid || !rendererModeValid|| !meshValid || !alignmentValid)
                    Errors += renderer.name + "\n";
            }
        }
        if (Errors != string.Empty)
        {
            EditorGUILayout.HelpBox("The following Particle Systems using this material with incorrect Renderer:\n" + Errors + "Use the Configure Particle Systems Renderer to fix this", MessageType.Error, true);
        }
    }
}
#endif