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


[RequireComponent (typeof(Renderer))]
public class WaveController : MonoBehaviour 
{
	public struct WaveInfo
	{
		public float height;
		public Vector3 normal;
		public Vector2 pushDir;
	}

	[SerializeField]
	private float meshSize = 20f;
	[SerializeField]
	private float waveGrowthRate = 0.5f;
	[SerializeField]
	private float waveWidth = 0.25f;
	[SerializeField]
	private float waveHeight = 0.5f;
	[SerializeField]
	private float fadePow = 2.0f;
	[SerializeField]
	private float sizePow = 2.0f;
	[SerializeField]
	private float shadowSize = 0.05f;
	[SerializeField]
	private float minShadowHeight = -2.0f;
	[SerializeField]
	private float maxShadowHeight = 6.0f;

	private Renderer renderer;
	private MaterialPropertyBlock block;
	private Vector4[] playerPoints = new Vector4[4];
	private Vector4[] waves = new Vector4[32];

	private int registeredPlayers = 0;
	private Transform[] playerTransforms = new Transform[4];

	// Use this for initialization
	void Start () 
	{
		block = new MaterialPropertyBlock();
		renderer = GetComponent<Renderer>();
		//block.SetColor("_Color", Color.red);

		block.SetFloat("_WaveWidth", waveWidth);
		block.SetFloat("_WaveHeight", waveHeight);
		block.SetFloat("_FadePow", fadePow);
		block.SetFloat("_SizePow", sizePow);
		block.SetFloat("_ShadowSize", shadowSize);
		block.SetFloat("_MinShadowHeight", minShadowHeight);
		block.SetFloat("_MaxShadowHeight", maxShadowHeight);

		// Shadows
		for(int i = 0; i<playerPoints.Length; i++) 
		{
			playerPoints[i] = new Vector4(0.25f*i, 1.0f, 0.25f*i, 1.0f);
		}
		block.SetVectorArray ("_PlayerPoints", playerPoints);

		// Waves
		for(int i = 0; i<waves.Length; i++) 
		{
			waves [i].z = 1.0f;
		}
		MakeWave (transform.position, 1f);
		block.SetVectorArray ("_Waves", waves);

		renderer.SetPropertyBlock (block);
	}
	
	// Update is called once per frame
	void Update () 
	{
        

		// Shadows
		for(int i = 0; i<registeredPlayers; i++) 
		{
			Vector2 relativePos = GetRelativePosition(playerTransforms[i].position);
			playerPoints[i] = new Vector4(relativePos.x, playerTransforms[i].position.y-1.0f, relativePos.y, 1.0f);
		}
		block.SetVectorArray ("_PlayerPoints", playerPoints);

		// Waves
		for(int i = 0; i<waves.Length; i++) 
		{
			waves [i].z += waveGrowthRate * Time.deltaTime;
		}
		block.SetVectorArray ("_Waves", waves);

		renderer.SetPropertyBlock (block);
	}

	void OnDrawGizmosSelected()
	{
		int res = 24;
		Vector3 startPos = transform.position + meshSize * new Vector3 (-0.5f, 0.0f, -0.5f);
		float offset = meshSize/((float)res - 1);
		// Gizmos.color = new Color(1, 1, 0, 1.0F);
		for(int z = 0; z < res; z++)
		{
			for(int x = 0; x < res; x++)
			{
				Vector3 rPos = startPos + new Vector3 (offset*x, 0.0f, offset*z);
				WaveController.WaveInfo wave = GetWaveAtPosition (rPos);
				Gizmos.color = new Color(0.0f, wave.normal.x*wave.height, wave.normal.z*wave.height, 1.0f);
				Gizmos.DrawRay(rPos+Vector3.up*wave.height, wave.normal*wave.height*1.0f);
				Gizmos.DrawSphere (rPos+Vector3.up*wave.height, 0.05f);
			}
		}
	}

	public void MakeWave( Vector3 worldPos, float strength)
	{
		Vector2 relativePos = GetRelativePosition(worldPos);
		waves[GetNewWaveIndex()] = new Vector4(relativePos.x, relativePos.y, 0.0f, strength);
	}

	private Vector2 GetRelativePosition(Vector3 worldPos)
	{
		return new Vector2(worldPos.x - transform.position.x, worldPos.z - transform.position.z)/meshSize + 0.5f*Vector2.one;
	}

	private float SmoothStep(float edge0, float edge1, float x)
	{
		float t;
		t = Mathf.Clamp01((x - edge0) / (edge1 - edge0));
		return t * t * (3.0f - 2.0f * t);
	}

	public void RegisterPlayerTransform(Transform player)
	{
		playerTransforms[registeredPlayers++] = player;
	}

	public WaveInfo GetWaveAtPosition(Vector3 worldPos)
	{
		WaveInfo result = new WaveInfo ();
		Vector2 relativePos = GetRelativePosition(worldPos);
		//print (relativePos);
		result.height = 0f;
		result.pushDir = new Vector2(0.0f, 0.0f); 
		Vector2 tangeant = new Vector2(0.0f, 0.0f);
		const float PI = 3.14159f;
		float distFromMid = Mathf.Clamp01 (5.0f * (0.5f * Vector2.one - relativePos).magnitude * 2.0f - 4.0f);
		//print (distFromMid);

		// Waves		
		for(int i = 0; i<waves.Length; i++)
		{
			if (waves [i].z >= 1.0f)
				continue;
			Vector2 dirFromWave = (relativePos - (Vector2)waves [i]).normalized;
			result.pushDir += dirFromWave;
			float distFromWave = ((Vector2)waves [i] - relativePos).magnitude;
			float steppedWave = SmoothStep (distFromWave, distFromWave + waveWidth, 1.0f-Mathf.Pow (1.0f-waves [i].z, sizePow));

			float shapedWave = 0.5f * (Mathf.Sin (steppedWave * 2.0f * PI - 0.5f * PI) + 1.0f);
			float finalWave = shapedWave * waves [i].w * Mathf.Max (1.0f - Mathf.Pow (waves [i].z, fadePow), 0.0f);// * (1.0f - Mathf.Pow (distFromMid, 4.0f));
			result.height = Mathf.Max (result.height + 0.5f * finalWave, finalWave);
			tangeant += (relativePos - (Vector2)waves [i]).normalized * Mathf.Sin (steppedWave * 2.0f * PI);

		}
		//tangeant = Vector2.ClampMagnitude (tangeant, 1.0f);
		result.normal = new Vector3 (tangeant.x, 1.0f, tangeant.y).normalized;
		return result;
	}

	private int GetNewWaveIndex()
	{
		int newWaveIndex = 0;
		float biggestWave = float.MinValue;
		for(int i = 0; i<waves.Length; i++) 
		{
			if (waves [i].z > 1f) 
				return i;
			
			if (waves [i].z > biggestWave) 
			{
				newWaveIndex = i;
			}
		}

		return newWaveIndex;
	}
}
