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

public class RippleSystem : MonoBehaviour
{
	[SerializeField]
	private bool rainDrops = false;
	[SerializeField]
	private float dropSize = 1;
	[SerializeField]
	private float damping = 1;
	[SerializeField]
	private RenderTexture _bufferCurrent;
	[SerializeField]
	private Shader waterShader;
	[SerializeField]
	private bool dropsFromClicks;
	[SerializeField]
	private float minRigidbodyVelocityForDrop;
	//
	//
	private int _waterSizeWidth;
	private int _waterSizeHeight;
	private RenderTextureFormat _format = RenderTextureFormat.ARGB32;
	private int _count = 0;
	private Dictionary<Shader, Material> _shaderMap = new Dictionary<Shader, Material>();
	private RenderTexture _bufferPrev;

	private new Collider collider;

	private Material rippleMaterial;

	private void Awake()
	{
		this.useGUILayout = false;
		collider = GetComponent<Collider>();
		rippleMaterial = new Material(waterShader);
	}

	private void Start()
	{
		RenderTexture.active = _bufferPrev;
		GL.Clear(true, true, Color.grey);
		RenderTexture.active = null;

		RenderTexture.active = _bufferCurrent;
		GL.Clear(true, true, Color.grey);
		RenderTexture.active = null;
	}

	private void OnEnable()
	{
		_waterSizeWidth = _bufferCurrent.width;
		_waterSizeHeight = _bufferCurrent.height;
		_format = _bufferCurrent.format;

		_bufferPrev = new RenderTexture(_waterSizeWidth, _waterSizeHeight, 0, _format, RenderTextureReadWrite.Linear);
		_bufferPrev.filterMode = _bufferCurrent.filterMode;

		GetComponent<Renderer>().material.mainTexture = _bufferCurrent;
	}

	private void OnDestroy()
	{
		Destroy(GetComponent<Renderer>().material);
		Destroy(_bufferPrev);

		foreach (KeyValuePair<Shader, Material> item in _shaderMap)
		{
			Destroy(item.Value);
		}

		_shaderMap.Clear();
	}

	private void Update()
	{
		damping = Mathf.Clamp(damping, 0, 1);

		rippleMaterial.SetFloat("_DropSize", dropSize);
		rippleMaterial.SetFloat("_Damping", damping);

		var scratchRT = RenderTexture.GetTemporary(_waterSizeWidth, _waterSizeHeight, 0, _format, RenderTextureReadWrite.Linear);
		scratchRT.filterMode = _bufferCurrent.filterMode;

		RenderTexture.active = scratchRT;
		GL.Clear(true, true, Color.grey);
		RenderTexture.active = null;

		if(Input.GetMouseButton(0) && dropsFromClicks)
		{
			var cam = Camera.main;
			RaycastHit info;
			if(collider.Raycast(cam.ScreenPointToRay(Input.mousePosition), out info, float.MaxValue))
			{
				Vector2 uv;
				if(collider is MeshCollider)
				{
					uv = info.textureCoord;
				}
				else
				{
					// plane is from -0.5 to 0.5
					var bounds = collider.bounds;
					uv = new Vector2(Mathf.InverseLerp(bounds.min.x, bounds.max.x, info.point.x), Mathf.InverseLerp(bounds.min.z, bounds.max.z, info.point.z));
//					Vector3 point = collider.transform.localToWorldMatrix.MultiplyPoint(info.point);
//					point += new Vector3(0.5f, 0, 0.5f);
//					uv = new Vector2(point.x, point.z);
				}

				rippleMaterial.SetVector("_MousePos", uv);
				Graphics.Blit(_bufferCurrent, scratchRT, rippleMaterial, 0);
				Graphics.Blit(scratchRT, _bufferCurrent, rippleMaterial, 0);
			}
		}

		// Raindrops
		if(rainDrops)
		{
			rippleMaterial.SetVector("_MousePos", Random.onUnitSphere);
			Graphics.Blit(_bufferCurrent, scratchRT, rippleMaterial, 0);
			Graphics.Blit(scratchRT, _bufferCurrent, rippleMaterial, 0);
		}

		UpdateWater();

		RenderTexture.ReleaseTemporary(scratchRT);
	}

	private void UpdateWater()
	{
		var bufferOne = (_count % 2 == 0) ? _bufferCurrent : _bufferPrev;
		var bufferTwo = (_count % 2 == 0) ? _bufferPrev : _bufferCurrent;
		_count++;

		rippleMaterial.SetTexture("_PrevTex", bufferTwo);
		Graphics.Blit(bufferOne, bufferTwo, rippleMaterial, 1);
	}

	private void OnTriggerStay(Collider col)
	{
		var rb = col.GetComponent<Rigidbody>();
		if(rb != null)
		{
//			Debug.Log(rb.velocity.magnitude);
			if(rb.velocity.magnitude >= minRigidbodyVelocityForDrop)
			{
				var pos = col.transform.position;
				CreateDropAtPosition(pos, dropSize);
			}
		}
	}

	public void CreateDropAtPosition(Vector3 pos, float customDropSize)
	{
		rippleMaterial.SetFloat("_DropSize", customDropSize);
		var scratchRT = RenderTexture.GetTemporary(_waterSizeWidth, _waterSizeHeight, 0, _format, RenderTextureReadWrite.Linear);
		scratchRT.filterMode = _bufferCurrent.filterMode;

		RenderTexture.active = scratchRT;
		GL.Clear(true, true, Color.grey);
		RenderTexture.active = null;

		var bounds = collider.bounds;
		var uv = new Vector2(Mathf.InverseLerp(bounds.min.x, bounds.max.x, pos.x), Mathf.InverseLerp(bounds.min.z, bounds.max.z, pos.z));
		rippleMaterial.SetVector("_MousePos", uv);
		Graphics.Blit(_bufferCurrent, scratchRT, rippleMaterial, 0);
		Graphics.Blit(scratchRT, _bufferCurrent, rippleMaterial, 0);
	}
}
