// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
using System;
using ClassicalSharp.Physics;
using OpenTK;
namespace ClassicalSharp.Entities {
	
	///  Entity component that plays block step sounds. 
	public sealed class SoundComponent {
		LocalPlayer p;
		Game game;		
		Predicate checkSoundNonSolid, checkSoundSolid;
		
		public SoundComponent(Game game, Entity entity) {
			this.game = game;
			p = (LocalPlayer)entity;
			checkSoundNonSolid = CheckSoundNonSolid;
			checkSoundSolid = CheckSoundSolid;
			lastSoundPos = -Utils.MaxPos();
		}
		
		Vector3 lastSoundPos;
		public void Tick(bool wasOnGround) {
			Vector3 soundPos = p.nextPos;
			GetSound();
			if (!anyNonAir) soundPos = Utils.MaxPos();
			
			if (p.onGround && (DoPlaySound(soundPos) || !wasOnGround)) {
				game.AudioPlayer.PlayStepSound(sndType);
				lastSoundPos = soundPos;
			}
		}
		
		bool DoPlaySound(Vector3 soundPos) {
			float distSq = (lastSoundPos - soundPos).LengthSquared;
			bool enoughDist = distSq > 1.75f * 1.75f;
			// just play every certain block interval when not animating
			if (p.curSwing < 0.999f) return enoughDist;
			
			// have our legs just crossed over the '0' point?
				float oldLegRot;
				float newLegRot;
			if (game.Camera.IsThirdPerson) {
				oldLegRot = (float)Math.Cos(p.anim.walkTimeO);
				newLegRot = (float)Math.Cos(p.anim.walkTimeN);
			}
			else {
				oldLegRot = (float)Math.Sin(p.anim.walkTimeO);
				newLegRot = (float)Math.Sin(p.anim.walkTimeN);
			}
			return Math.Sign(oldLegRot) != Math.Sign(newLegRot);
		}
		
		bool anyNonAir = false;
		SoundType sndType = SoundType.None;
		void GetSound() {
			Vector3 pos = p.nextPos;
			AABB bounds = p.Bounds;
			sndType = SoundType.None;
			anyNonAir = false;
			
			// first check surrounding liquids/gas for sounds
			p.TouchesAny(bounds, checkSoundNonSolid);
			if (sndType != SoundType.None) return;
			
			// then check block standing on
			pos.Y -= 0.01f;
			Vector3I feetPos = Vector3I.Floor(pos);
			byte blockUnder = game.World.SafeGetBlock(feetPos);
			float maxY = feetPos.Y + game.BlockInfo.MaxBB[blockUnder].Y;
			
			SoundType typeUnder = game.BlockInfo.StepSounds[blockUnder];
			CollideType collideType = game.BlockInfo.Collide[blockUnder];
			if (maxY >= pos.Y && collideType == CollideType.Solid && typeUnder != SoundType.None) {
				anyNonAir = true; sndType = typeUnder; return;
			}
			
			// then check all solid blocks at feet
			bounds.Max.Y = bounds.Min.Y = pos.Y;
			p.TouchesAny(bounds, checkSoundSolid);
		}
		
		bool CheckSoundNonSolid(byte b) {
			SoundType newType = game.BlockInfo.StepSounds[b];
			CollideType collide = game.BlockInfo.Collide[b];
			if (newType != SoundType.None && collide != CollideType.Solid)
				sndType = newType;
			
			if (game.BlockInfo.Draw[b] != DrawType.Gas)
				anyNonAir = true;
			return false;
		}
		
		bool CheckSoundSolid(byte b) {
			SoundType newType = game.BlockInfo.StepSounds[b];
			if (newType != SoundType.None) sndType = newType;
			
			if (game.BlockInfo.Draw[b] != DrawType.Gas)
				anyNonAir = true;
			return false;
		}
	}
}