using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using DG.Tweening;

public enum AttackType {
	none,
	kick,
	punch,
	shout
}

public class Player : MonoBehaviour
{
	[SerializeField]
	private float movementSpeed = 2;

	[SerializeField]
	[Range(0f, 1f)]
	private float spacingLeft = 0.9f;
	[SerializeField]
	[Range(-1f, 1f)]
	private float spacingRight = -0.3f;


	[SerializeField]
	private float progressPerSecond = 0.01f;

	[SerializeField]
	private float scrollSpeedProgressDiv = 1f;
	
	[SerializeField]
	private float aktiMeterLossPerSecond = 0.02f;

	[SerializeField]
	private float attackCooldown = 1f;

	[SerializeField]
	private float slowDownFactor = 0.0f;

	[SerializeField]
	private float beerPerBottle = 0.2f;

	[SerializeField]
	private float beerCostPerAttack = 0.2f;

	[SerializeField]
	private float activityPerAttack = 0.2f;

	[Header("Attack Ranges")]
	[SerializeField]
	private float attackSizeKick = 1f;
	[SerializeField]
	private float attackRangeKick = 1f;
	
	[SerializeField]
	private float attackSizePunch = 1f;
	[SerializeField]
	private float attackRangePunch = 1f;

	[SerializeField]
	private float attackSizeShout = 1f;
	[SerializeField]
	private float attackRangeShout = 1f;

	[SerializeField]
	private Camera cam;

	[SerializeField]
	private Animator animator;
	[SerializeField]
	private AnimationCurve progressCurve = null;

	public SoundBoard sounds;

	private float speed = 0f;

	private float progress = 0f;

	private float aktiMeter = 1.0f;
	private float beerMeter = 1.0f;

	private float slowNotificationCooldown = 0f;

	public float parallaxOffset { get; private set; }

	private AttackType queuedAttack = AttackType.none;

	private float attackCooldownLeft = 0f;

	private bool blockMovement = false;

	void Start() {
		if(cam==null) {
			cam = Camera.main;
		}

		sounds = GetComponent<SoundBoard>();
	}
	public float GetAktiMeter() {
		return progressCurve==null ? aktiMeter : progressCurve.Evaluate(aktiMeter);
	}
	public float GetBeerMeter() {
		return progressCurve==null ? beerMeter : progressCurve.Evaluate(beerMeter);
	}
	public float GetProgress() {
		return progressCurve==null ? progress : 1f-progressCurve.Evaluate(1f-progress);
	}

	public void queueAttack(AttackType at) {
		queuedAttack = at; 
	}

	public void moveLevel(float delta) {
		parallaxOffset = delta / ParallaxScroller.SPEED_FACTOR;
	}

	private float addClamp(float val, float b) {
		return Mathf.Clamp(val+b, 0f, 1f);
	}

	public void OnTriggerEnter2D(Collider2D other) {
		if(other.GetComponent<PowerupBeer>()!=null) {
			Destroy(other.gameObject);
			drinkBeer();
		}
	}

	private void drinkBeer() {
		Debug.Log("drank beer");
		// TODO: play animation
		if(sounds!=null)
			sounds.play("drink");
		beerMeter = addClamp(beerMeter, beerPerBottle);
	}


	public void move(float moveInput) {
		if(Time.timeScale<=0.00001f) {
			moveLevel(0);
			return;
		}
		if(blockMovement)
			moveInput = 0f;

		float camHalfWidth = cam.orthographicSize * Screen.width / (float)Screen.height;
		float camXPos = cam.transform.position.x;
		float boundLeft = camXPos - camHalfWidth * spacingLeft;
		float boundRight = camXPos - camHalfWidth* spacingRight;

		float movement = Mathf.Clamp(moveInput * 1.5f - 0.5f, -1, 1) * movementSpeed * Time.deltaTime;

		animator.SetFloat("speed", Mathf.Abs(moveInput));

		if(beerMeter <= 0.001f) {
			movement *= slowDownFactor;
		}

		speed = Mathf.Lerp(speed, movement, Mathf.Min(1f, Time.deltaTime*10f));

		var p = transform.position;
		p.x = Mathf.Max(boundLeft, p.x+speed);
		if(p.x > boundRight) {
			moveLevel(p.x - boundRight);
			p.x = boundRight;
		} else {
			moveLevel(0);
		}

		transform.position = p;
	}

    void Update()
    {
		float levelSpeed = 1f + parallaxOffset / scrollSpeedProgressDiv;
		progress  = addClamp(progress, progressPerSecond * Time.deltaTime * levelSpeed);
		aktiMeter = addClamp(aktiMeter, -aktiMeterLossPerSecond * Time.deltaTime * levelSpeed);

		if(sounds!=null && beerMeter<=0.001f && slowNotificationCooldown<=0f) {
			sounds.play("slow");
			slowNotificationCooldown = 8f;
		}
		
		if(slowNotificationCooldown>0f)
			slowNotificationCooldown -= Time.deltaTime;

		if(attackCooldownLeft>0f) {
			attackCooldownLeft -= Time.deltaTime;
		}
		if(queuedAttack!=AttackType.none) {
			if(attackCooldownLeft <= 0f) {
				StartCoroutine(attack(queuedAttack));
				attackCooldownLeft = attackCooldown;
			}
			queuedAttack = AttackType.none;
		}

		if(aktiMeter<=0f && GetProgress()<0.999f) {
			// game over
			if(sounds!=null)
				sounds.play("gameover");
			
			var music = Camera.main.GetComponent<AudioSource>();
			if(music!=null)
				music.Stop();
			
			transform.DORotate(new Vector3(0,0,-70f), 0.3f).SetUpdate(true);
			StartCoroutine("onGameOver");
		}
    }

	private IEnumerator onGameOver() {
		Time.timeScale = 0f;

		yield return new WaitForSecondsRealtime(1f);

		var fade = cam.GetComponent<ScreenTransitionImageEffect>();
		while(fade.maskValue<1f) {
			yield return new WaitForSecondsRealtime(1f/30f);
			fade.maskValue += 1f/30f / 10f;
		}
		
		yield return new WaitForSecondsRealtime(1f);
		Time.timeScale = 1f;
		Scene scene = SceneManager.GetActiveScene();
		SceneManager.LoadScene(scene.name);
	}

	private IEnumerator attack(AttackType type) {
		blockMovement = true;
		beerMeter = addClamp(beerMeter, -beerCostPerAttack);

		float attackSize = 1f;
		float attackRange = 1f;
		switch(type) {
			case AttackType.kick:
				attackSize = attackSizeKick;
				attackRange = attackRangeKick;
				animator.ResetTrigger("kick");
				animator.SetTrigger("kick");
				yield return new WaitForSecondsRealtime(0.035f);
				break;
			case AttackType.punch:
				attackSize = attackSizePunch;
				attackRange = attackRangePunch;
				animator.ResetTrigger("fist");
				animator.SetTrigger("fist");
				yield return new WaitForSecondsRealtime(0.035f);
				break;
			case AttackType.shout:
				attackSize = attackSizeShout;
				attackRange = attackRangeShout;
				animator.ResetTrigger("scream");
				animator.SetTrigger("scream");
				break;
		}

		var hits = Physics2D.CircleCastAll(new Vector2(transform.position.x+attackSize, transform.position.y), attackSize, Vector2.right, attackRange);

		var success = false;
		foreach(RaycastHit2D hit in hits) {
			var target = hit.transform.GetComponent<AttackTarget>();
			if(target!=null && target.wouldHit(type)) {
				success = true;
			}
		}

		switch(type) {
			case AttackType.kick:
				sounds.play(success ? "kick" : "kick_miss");
				yield return new WaitForSecondsRealtime(0.3f);
				break;
			case AttackType.punch:
				sounds.play(success ? "punch" : "punch_miss");
				yield return new WaitForSecondsRealtime(0.15f);
				break;
			case AttackType.shout:
				sounds.play("shout");
				yield return new WaitForSecondsRealtime(0.3f);
				break;
		}

		foreach(RaycastHit2D hit in hits) {
			var target = hit.transform.GetComponent<AttackTarget>();
			if(target!=null && target.attack(type)) {
				aktiMeter = addClamp(aktiMeter, activityPerAttack);
			}
		}

		yield return new WaitForSecondsRealtime(0.3f);
		blockMovement = false;
	}

}
