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

#if UNITY_EDITOR
	using UnityEditor;
#endif

namespace LuxURPEssentials
{

	[System.Serializable]
    public enum RTSize {
    	_128 = 128,
        _256 = 256,
        _512 = 512
    }

    [System.Serializable]
    public enum RTFormat {
    	ARGB32 = 0,
        ARGBHalf = 1
    }

    [System.Serializable]
    public enum GustMixLayer {
    	Layer_0 = 0,
        Layer_1 = 1,
        Layer_2 = 2
    }

	[ExecuteInEditMode]
	//[ExecuteAlways]
	[RequireComponent(typeof(WindZone))]
	//[HelpURL("https://docs.google.com/document/d/1ck3hmPzKUdewHfwsvmPYwSPCP8azwtpzN7aOLJHvMqE/edit#heading=h.wnnhm4pxp610")]
	public class LuxURP_Wind : MonoBehaviour {
	//	using order to fix header/button issue
		[Space(5)]
		[LuxURP_HelpBtn("h.wnnhm4pxp610")]
		[Space(-5)]

		public bool UpdateInEditMode = false;
		
		[Header("Render Texture Settings")]

		public RTSize Resolution = RTSize._256;
		public RTFormat Format = RTFormat.ARGB32;
		public Texture WindBaseTex;
		public Shader WindCompositeShader;
		
		[Header("Wind Multipliers")]
		public float Grass = 1.0f;
		public float Foliage = 1.0f; 

		[Header("Wind Speed and Size")]
		[Tooltip("Base Wind Speed in km/h at Main = 1 (WindZone)")]
        public float BaseWindSpeed = 15;
        [Tooltip("Size of the Wind RenderTexture in World Space")]
        public float SizeInWorldSpace = 50;
		[Space(5)]
		public float speedLayer0 = 1.0f;
		public float speedLayer1 = 1.137f;
		public float speedLayer2 = 1.376f;

		[Header("Noise")]
		public int GrassGustTiling = 4;
		public float GrassGustSpeed = 0.278f;
		public GustMixLayer LayerToMixWith = GustMixLayer.Layer_1; 
		
		[Header("Jitter")]
		public float JitterFrequency = 3.127f;
		public float JitterHighFrequency = 21.0f;

	
		private RenderTexture WindRenderTexture;
		private Material m_material;

		private Vector2 uvs = new Vector2(0,0);
		private Vector2 uvs1 = new Vector2(0,0);
		private Vector2 uvs2 = new Vector2(0,0);
		private Vector2 uvs3 = new Vector2(0,0);

		private int WindRTPID;

		private Transform trans;
		private WindZone windZone;
		private float mainWind;
		private float turbulence;

		private int LuxLWRPWindDirSizePID;
		private int LuxLWRPWindStrengthMultipliersPID;
		private int LuxLWRPSinTimePID;
		private int LuxLWRPGustPID;
		private int LuxLWRPGustMixLayerPID;

		private int LuxLWRPWindUVsPID;
		private int LuxLWRPWindUVs1PID;
		private int LuxLWRPWindUVs2PID;
		private int LuxLWRPWindUVs3PID;

		private int previousRTSize;
		private int previousRTFormat;

		private Vector4 WindDirectionSize = Vector4.zero;

		private static Vector3[] MixLayers = new [] { new Vector3(1f,0f,0f), new Vector3(0f,1f,0f), new Vector3(0f,0f,1f)  };

		#if UNITY_EDITOR
			private double lastTimeStamp = 0.0;
		#endif

		void OnEnable () {
			if(WindCompositeShader == null) {
				WindCompositeShader = Shader.Find("Hidden/Lux URP WindComposite");
			}
			if (WindBaseTex == null ) {
				WindBaseTex = Resources.Load("Lux URP default wind base texture") as Texture;
			}
			SetupRT();
			GetPIDs();
			trans = this.transform;
			windZone = trans.GetComponent<WindZone>();

			previousRTSize = (int)Resolution;
			previousRTFormat = (int)Format;

			#if UNITY_EDITOR
				EditorApplication.update += OnEditorUpdate;
			#endif
		}


		void OnDisable () {
			if (WindRenderTexture != null) {
				WindRenderTexture.Release();
				UnityEngine.Object.DestroyImmediate(WindRenderTexture);
			}
			if (m_material != null) {
				UnityEngine.Object.DestroyImmediate(m_material);
				m_material = null;
			}
			if (WindBaseTex != null) {
				WindBaseTex = null;
			}

			#if UNITY_EDITOR
				EditorApplication.update -= OnEditorUpdate;
			#endif
		}

		#if UNITY_EDITOR
			void OnEditorUpdate() {
				if(!Application.isPlaying && UpdateInEditMode) {
					Update();
				//	Unity 2019.1.10 on macOS using Metal also needs this
					SceneView.RepaintAll(); 
				}
			}
		#endif

		void SetupRT () {
			if (WindRenderTexture == null || m_material == null)
	        {
	        	var rtf = ((int)Format == 0) ? RenderTextureFormat.ARGB32 : RenderTextureFormat.ARGBHalf;
	            WindRenderTexture = new RenderTexture((int)Resolution, (int)Resolution, 0, rtf, RenderTextureReadWrite.Linear );
	            WindRenderTexture.useMipMap = true;
	            WindRenderTexture.wrapMode = TextureWrapMode.Repeat;
	            m_material = new Material(WindCompositeShader);
	        }
		}

		void GetPIDs () {
			WindRTPID = Shader.PropertyToID("_LuxLWRPWindRT");
			LuxLWRPWindDirSizePID = Shader.PropertyToID("_LuxLWRPWindDirSize");
			LuxLWRPWindStrengthMultipliersPID = Shader.PropertyToID("_LuxLWRPWindStrengthMultipliers");
			LuxLWRPSinTimePID = Shader.PropertyToID("_LuxLWRPSinTime");
			LuxLWRPGustPID = Shader.PropertyToID("_LuxLWRPGust");
			LuxLWRPWindUVsPID = Shader.PropertyToID("_LuxLWRPWindUVs");
			LuxLWRPWindUVs1PID = Shader.PropertyToID("_LuxLWRPWindUVs1");
			LuxLWRPWindUVs2PID = Shader.PropertyToID("_LuxLWRPWindUVs2");
			LuxLWRPWindUVs3PID = Shader.PropertyToID("_LuxLWRPWindUVs3");
			LuxLWRPGustMixLayerPID = Shader.PropertyToID("_GustMixLayer");
		}

		void OnValidate () {
			if(WindCompositeShader == null) {
				WindCompositeShader = Shader.Find("Hidden/Lux LWRP WindComposite");
			}
			if (WindBaseTex == null ) {
				WindBaseTex = Resources.Load("Default wind base texture") as Texture;
			}
			if ( (previousRTSize != (int)Resolution ) || ( previousRTFormat != (int)Format ) ) {
				var rtf = ((int)Format == 0) ? RenderTextureFormat.ARGB32 : RenderTextureFormat.ARGBHalf;
				WindRenderTexture = new RenderTexture((int)Resolution, (int)Resolution, 0, rtf, RenderTextureReadWrite.Linear );
	            WindRenderTexture.useMipMap = true;
	            WindRenderTexture.wrapMode = TextureWrapMode.Repeat;
			}
		}
		
		void Update () {

		//	Get wind settings from WindZone
			mainWind = windZone.windMain;
			turbulence = windZone.windTurbulence;
			
			float delta = Time.deltaTime;

			#if UNITY_EDITOR
				if(!Application.isPlaying) {
					delta = (float)(EditorApplication.timeSinceStartup - lastTimeStamp);
					lastTimeStamp = EditorApplication.timeSinceStartup;
				}
			#endif

			WindDirectionSize.x = trans.forward.x;
			WindDirectionSize.y = trans.forward.y;
			WindDirectionSize.z = trans.forward.z;
			WindDirectionSize.w = 1.0f / SizeInWorldSpace;

			var windVec = new Vector2(WindDirectionSize.x, WindDirectionSize.z ) * delta * (BaseWindSpeed * 0.2777f * WindDirectionSize.w); // * mainWind);

			uvs -= windVec * speedLayer0;
			uvs.x = uvs.x - (int)uvs.x;
			uvs.y = uvs.y - (int)uvs.y;

			uvs1 -= windVec * speedLayer1;
			uvs1.x = uvs1.x - (int)uvs1.x;
			uvs1.y = uvs1.y - (int)uvs1.y;

			uvs2 -= windVec * speedLayer2;
			uvs2.x = uvs2.x - (int)uvs2.x;
			uvs2.y = uvs2.y - (int)uvs2.y;

			uvs3 -= windVec * GrassGustSpeed 			* turbulence;
			uvs3.x = uvs3.x - (int)uvs3.x;
			uvs3.y = uvs3.y - (int)uvs3.y;

		//	Set global shader variables for grass and foliage shaders
			Shader.SetGlobalVector(LuxLWRPWindDirSizePID, WindDirectionSize);

			Vector2 tempWindstrengths;
			tempWindstrengths.x = Grass * mainWind;
			tempWindstrengths.y = Foliage * mainWind;
			Shader.SetGlobalVector(LuxLWRPWindStrengthMultipliersPID, tempWindstrengths );
		//	Use clamped turbulence as otherwise wind direction might get "reversed"
			Shader.SetGlobalVector(LuxLWRPGustPID, new Vector2(GrassGustTiling, Mathf.Clamp( turbulence + 0.5f, 0.0f, 1.5f))  );	
		//	Jitter frequncies and strength
			Shader.SetGlobalVector(LuxLWRPSinTimePID, new Vector4(
				(float)Math.Sin(Time.time * JitterFrequency),
				(float)Math.Sin(Time.time * JitterFrequency * 0.2317f + 2.0f * Mathf.PI),
				(float)Math.Sin(Time.time * JitterHighFrequency),
				turbulence * 0.1f
			));
		

		//	Set UVs
			Shader.SetGlobalVector(LuxLWRPWindUVsPID, uvs);
			Shader.SetGlobalVector(LuxLWRPWindUVs1PID, uvs1);
			Shader.SetGlobalVector(LuxLWRPWindUVs2PID, uvs2);
			Shader.SetGlobalVector(LuxLWRPWindUVs3PID, uvs3);

		//	Set Mix Layer
			Shader.SetGlobalVector(LuxLWRPGustMixLayerPID, MixLayers[(int)LayerToMixWith]);

		#if UNITY_EDITOR
			if (m_material != null && WindRenderTexture != null ) {
		#endif
				Graphics.Blit(WindBaseTex, WindRenderTexture, m_material);
				WindRenderTexture.SetGlobalShaderProperty("_LuxLWRPWindRT"); // only accepts strings...
		#if UNITY_EDITOR
			}
		#endif
			
		}

	#if UNITY_EDITOR
		void OnRenderObject() {
			//Update();
		}
	#endif
	}
}