// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
using System;
using ClassicalSharp.Model;
using OpenTK;
namespace ClassicalSharp.Entities {
	///  Entity component that performs management of hack states. 
	public sealed class HacksComponent {
		
		Game game;
		Entity entity;
		public HacksComponent(Game game, Entity entity) {
			this.game = game;
			this.entity = entity;
		}
		
		public byte UserType;
		
		///  The speed that the player move at, relative to normal speed,
		/// when the 'speeding' key binding is held down. 
		public float SpeedMultiplier = 10;		
				
		///  Whether blocks that the player places that intersect themselves
		/// should cause the player to be pushed back in the opposite direction of the placed block. 
		public bool PushbackPlacing;
		
		///  Whether the player should be able to step up whole blocks, instead of just slabs. 
		public bool FullBlockStep;
		
		///  Whether the player has allowed hacks usage as an option.
		/// Note that all 'can use X' set by the server override this. 
		public bool Enabled = true;
		///  Whether the player is allowed to use any type of hacks. 
		public bool CanAnyHacks = true;
		///  Whether the player is allowed to use the types of cameras that use third person. 
		public bool CanUseThirdPersonCamera = true;
		///  Whether the player is allowed to increase its speed beyond the normal walking speed. 
		public bool CanSpeed = true;
		///  Whether the player is allowed to fly in the world. 
		public bool CanFly = true;
		///  Whether the player is allowed to teleport to their respawn coordinates. 
		public bool CanRespawn = true;
		///  Whether the player is allowed to pass through all blocks. 
		public bool CanNoclip = true;
		///  Whether the player is allowed to use pushback block placing. 
		public bool CanPushbackBlocks = true;		
		///  Whether the player is allowed to see all entity name tags. 
		public bool CanSeeAllNames = true;					
		///  Whether the player is allowed to double jump. 
		public bool CanDoubleJump = true;	
		///  Maximum speed the entity can move at horizontally when CanSpeed is false. 
		public float MaxSpeedMultiplier = 1;
		
		///  Whether the player should slide after letting go of movement buttons in noclip.  
		public bool NoclipSlide = true;		
		///  Whether the player has allowed the usage of fast double jumping abilities. 
		public bool WOMStyleHacks = false;
		
		///  Whether the player currently has noclip on. 
		public bool Noclip;
		///  Whether the player currently has fly mode active. 
		public bool Flying;
		///  Whether the player is currently flying upwards. 
		public bool FlyingUp;
		///  Whether the player is currently flying downwards. 
		public bool FlyingDown;
		///  Whether the player is currently walking at base speed * speed multiplier. 
		public bool Speeding;
		///  Whether the player is currently walking at base speed * 0.5 * speed multiplier. 
		public bool HalfSpeeding;
		
		public bool CanJumpHigher { get { return Enabled && CanAnyHacks && CanSpeed; } }
		
		///  Parses hack flags specified in the motd and/or name of the server. 
		///  Recognises +/-hax, +/-fly, +/-noclip, +/-speed, +/-respawn, +/-ophax, and horspeed=xyz 
		public void ParseHackFlags(string name, string motd) {
			string joined = name + motd;
			SetAllHacks(true);
			MaxSpeedMultiplier = 1;
			// By default (this is also the case with WoM), we can use hacks.
			if (joined.Contains("-hax")) SetAllHacks(false);
			
			ParseFlag(b => CanFly = b, joined, "fly");
			ParseFlag(b => CanNoclip = b, joined, "noclip");
			ParseFlag(b => CanSpeed = b, joined, "speed");
			ParseFlag(b => CanRespawn = b, joined, "respawn");
			if (UserType == 0x64)
				ParseFlag(b => SetAllHacks(b), joined, "ophax");
			ParseHorizontalSpeed(joined);
		}
		
		void ParseHorizontalSpeed(string joined) {
			int start = joined.IndexOf("horspeed=", StringComparison.OrdinalIgnoreCase);
			if (start < 0) return;
			start += 9;
			
			int end = joined.IndexOf(' ', start);
			if (end < 0) end = joined.Length;
			
			string num = joined.Substring(start, end - start);
			float value = 0;
			if (!Utils.TryParseDecimal(num, out value) || value <= 0) return;
			MaxSpeedMultiplier = value;
		}
		
		void SetAllHacks(bool allowed) {
			CanAnyHacks = CanFly = CanNoclip = CanRespawn = CanSpeed =
				CanPushbackBlocks = CanUseThirdPersonCamera = allowed;
		}
		
		static void ParseFlag(Action action, string joined, string flag) {
			if (joined.Contains("+" + flag)) {
				action(true);
			} else if (joined.Contains("-" + flag)) {
				action(false);
			}
		}
		
		///  Sets the user type of this user. This is used to control permissions for grass,
		/// bedrock, water and lava blocks on servers that don't support CPE block permissions. 
		public void SetUserType(byte value) {
			bool isOp = value >= 100 && value <= 127;
			UserType = value;
			Inventory inv = game.Inventory;
			inv.CanPlace[Block.Bedrock] = isOp;
			inv.CanDelete[Block.Bedrock] = isOp;
			inv.CanPlace[Block.Water] = isOp;
			inv.CanPlace[Block.StillWater] = isOp;
			inv.CanPlace[Block.Lava] = isOp;
			inv.CanPlace[Block.StillLava] = isOp;
			CanSeeAllNames = isOp;
		}
		
		///  Disables any hacks if their respective CanHackX value is set to false. 
		public void CheckHacksConsistency() {
			if (!CanFly || !Enabled) { Flying = false; FlyingDown = false; FlyingUp = false; }
			if (!CanNoclip || !Enabled) Noclip = false;
			if (!CanSpeed || !Enabled) { Speeding = false; HalfSpeeding = false; }
			CanDoubleJump = CanAnyHacks && Enabled && CanSpeed;
			
			if (!CanUseThirdPersonCamera || !Enabled)
				game.CycleCamera();
		}
	}
}