/*
 * +================================================================================+
 *      __  ___  ______________  __  __  ______________   ____
 *     / / / / |/ /  _/_  __/\ \/ / / / / /_  __/  _/ /  / __/
 *    / /_/ /    // /  / /    \  / / /_/ / / / _/ // /___\ \
 *    \____/_/|_/___/ /_/     /_/  \____/ /_/ /___/____/___/
 *
 * +================================================================================+
 *
 *    "THE BEER-WARE LICENSE"
 *    -----------------------
 *
 *    As long as you retain this notice you can do whatever you want with this
 *    stuff. However it is expressly forbidden to sell or commercially distribute
 *    Unity Utils or parts of it apart from your games.
 *    If you meet any of the authors some day, and you think this stuff is
 *    worth it, you can buy them a beer in return.
 *
 *    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *    THE SOFTWARE.
 *
 * +================================================================================+
 *
 *    Authors:
 *    David Krummenacher (dave@timbookedtwo.com)
 *
 */

using System.Collections;
using UnityEngine;

namespace Utils {

	/// <summary>
	/// Coroutines container class.
	/// </summary>
	public abstract class Coroutines {

		/// <summary>
		/// Fades an alpha value.
		/// Shaders needs to support alpha. Set the rendering mode to transparent.
		/// </summary>
		/// <returns>The alpha.</returns>
		/// <param name="renderer">Renderer.</param>
		/// <param name="start">Start.</param>
		/// <param name="end">End.</param>
		/// <param name="time">Time.</param>
		/// <param name="function">Function.</param>
		public static IEnumerator FadeAlpha(Renderer renderer, float start, float end, float time, Ease.EaseFunctions function) {
			Color[] colors = new Color[renderer.materials.Length];
			for (int i = 0; i < renderer.materials.Length; i++) {
				colors[i] = renderer.materials[i].color;
			}
			
			for (float t = 0f; t < time; t += Time.deltaTime) {
				float a = Ease.GetEase(function, t / time, start, end);
				
				for (int i = 0; i < renderer.materials.Length; i++) {
					colors[i].a = a;
					renderer.materials[i].color = colors[i];
				}
				
				yield return null;
			}
			
			for (int i = 0; i < renderer.materials.Length; i++) {
				colors[i].a = end;
				renderer.materials[i].color = colors[i];
			}
		}
		
		/// <summary>
		/// Flickers.
		/// Shaders needs to support alpha. Set the rendering mode to transparent.
		/// </summary>
		/// <param name="renderer">Renderer.</param>
		/// <param name="times">The amount of times.</param>
		/// <param name="time">Time.</param>
		/// <param name="fadeout">If set to <c>true</c> fades out at the end.</param>
		/// <param name="destroy">If set to <c>true</c> destroys the game boject of the renderer.</param>
		public static IEnumerator Flicker(Renderer renderer, int times, float time, bool fadeout = false, bool destroy = false) {
			float amount = times;
			if (fadeout) amount += 0.5f;
			
			Color[] colors = new Color[renderer.materials.Length];
			for (int i = 0; i < renderer.materials.Length; i++) {
				colors[i] = renderer.materials[i].color;
			}
			
			for (float t = 0f; t < time; t += Time.deltaTime) {
				float a = 1f - Mathf.Abs(Mathf.Sin(t / time * amount * Mathf.PI));
				
				for (int i = 0; i < renderer.materials.Length; i++) {
					colors[i].a = a;
					renderer.materials[i].color = colors[i];
				}
				
				yield return null;
			}
			
			if (destroy) {
				GameObject.Destroy(renderer.gameObject);
			} else {
				for (int i = 0; i < renderer.materials.Length; i++) {
					colors[i].a = (fadeout) ? 0f : 1f;
					renderer.materials[i].color = colors[i];
				}
			}
		}
		
		/// <summary>
		/// Shakes.
		/// </summary>
		/// <param name="transform">Transform.</param>
		/// <param name="amount">Amount.</param>
		/// <param name="time">Time.</param>
		public static IEnumerator Shake(Transform transform, float amount, float time) {
			Vector3 originalPosition = transform.localPosition;
			
			for (float t = 0f; t < time; t += Time.deltaTime) {
				transform.localPosition = originalPosition + Random.insideUnitSphere * amount;
				yield return null;
			}
			
			transform.localPosition = originalPosition;
		}
		
		/// <summary>
		/// Shakes.
		/// </summary>
		/// <returns>The d.</returns>
		/// <param name="transform">Transform.</param>
		/// <param name="amount">Amount.</param>
		/// <param name="time">Time.</param>
		public static IEnumerator Shake2D(Transform transform, float amount, float time) {
			Vector2 originalPosition = transform.localPosition;
			
			for (float t = 0f; t < time; t += Time.deltaTime) {
				transform.localPosition = originalPosition + Random.insideUnitCircle * amount;
				yield return null;
			}
			
			transform.localPosition = originalPosition;
		}
		
		/// <summary>
		/// Sinus floats the transform in the given direction.
		/// </summary>
		/// <returns>The float.</returns>
		/// <param name="transform">Transform.</param>
		/// <param name="direction">Direction.</param>
		/// <param name="shakes">Shakes.</param>
		/// <param name="amount">Amount.</param>
		/// <param name="time">Time.</param>
		public static IEnumerator SinusFloat (Transform transform, Vector3 direction, int shakes, float amount, float time) {
			Vector3 originalPosition = transform.localPosition;
			
			for (float t = 0f; t < time; t += Time.deltaTime) {
				transform.localPosition = originalPosition + direction * amount *  Mathf.Sin(t / (time / shakes) * Mathf.PI);
				yield return null;
			}
			
			transform.localPosition = originalPosition;
		}

		// TODO: Add missing effects in here!
	}
}
