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

public class GlassEditor : ShaderGUI {

	string versionLabel = "v1.8";

	// Surface
	MaterialProperty _GrabpassTint = null;
	MaterialProperty _SpecularityTint = null;
	MaterialProperty _BaseColorTint = null;
	MaterialProperty _BaseColor = 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;

	// 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;

	// Render Settings
	MaterialProperty _ReflectionsToggle = null;
	MaterialProperty _SpecularToggle = 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;

    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;

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

        EditorGUI.BeginChangeCheck(); {
			
			// Surface
			MGUI.BoldLabel("Surface");
			MGUI.PropertyGroup(()=>{
				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, _BaseColor);
					MGUI.TextureSO(me, _BaseColor, _BaseColor.textureValue);
					me.TexturePropertySingleLine(Tips.metallicText, _MetallicMap, _Metallic);
					MGUI.TextureSO(me, _MetallicMap, _MetallicMap.textureValue);
					me.TexturePropertySingleLine(Tips.roughnessTexLabel, _RoughnessMap, _Roughness);
					MGUI.TextureSO(me, _RoughnessMap, _RoughnessMap.textureValue);
					me.TexturePropertySingleLine(Tips.occlusionTexLabel, _OcclusionMap, _OcclusionMap.textureValue ? _Occlusion : null);
					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);
						MGUI.ToggleFloat(me, Tips.meshRefractionText, _RefractVertexNormal, _RefractionIOR);
					});
				}
				MGUI.SpaceN2();
			});
			MGUI.Space10();

			// Rain
			MGUI.BoldLabel("Rain");
			MGUI.PropertyGroup(()=>{
				me.ShaderProperty(_RainMode, Tips.rainModeText);
				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);
				}
				MGUI.SpaceN2();
			});
			MGUI.Space10();

			// AreaLit
			if (Shader.Find("AreaLit/Standard") != null){
				MGUI.BoldLabel("AreaLit");
				MGUI.PropertyGroup(()=>{
					me.ShaderProperty(_AreaLitToggle, "Enable");
					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");
			}
			MGUI.Space10();

			// Render Settings
			MGUI.BoldLabel("Render Settings");
			MGUI.PropertyGroup(()=>{
				MGUI.PropertyGroup(()=>{
					me.ShaderProperty(_ReflectionsToggle, "Reflections");
					me.ShaderProperty(_SpecularToggle, "Specular Highlights");
					me.ShaderProperty(_LitBaseColor, Tips.litBaseColorText);
				});
				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);
					me.ShaderProperty(_Culling, "Culling Mode");
				});
				MGUI.SpaceN2();
			});
			MGUI.ToggleGroupEnd();
		}
		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){
		int rainMode = mat.GetInt("_RainMode");
		int blurMode = mat.GetInt("_BlurQuality");
		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", mat.GetInt("_RainMode") == 2);
		MGUI.SetKeyword(mat, "_RAINMODE_AUTO", mat.GetInt("_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);
	}

	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);
				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);
				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);
				break;
			default: break;
		}
	}

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