package com.rs2.game.content.combat.range;

import com.rs2.GameConstants;
import com.rs2.GameEngine;
import com.rs2.event.CycleEvent;
import com.rs2.event.CycleEventContainer;
import com.rs2.event.CycleEventHandler;
import com.rs2.game.content.combat.CombatConstants;
import com.rs2.game.npcs.Npc;
import com.rs2.game.npcs.NpcHandler;
import com.rs2.game.objects.Objects;
import com.rs2.game.players.Player;
import com.rs2.util.Misc;
import com.rs2.world.Boundary;
import com.rs2.world.clip.Region;

/**
 * Cannon
 * @author Andrew (Mr Extremez)
 */

public class DwarfCannon {

	public DwarfCannon(Player player2) {
		this.player = player2;
	}

	private Player player;

	public final int[] ITEM_PARTS = {6, 8, 10, 12};
	
	private final int[] OBJECT_PARTS = {7, 8, 9, 6};
	
	private final int ballsID = 2;

	public final int steelBarID = 2353;

	public boolean settingUp = false;
	
	private int setUpStage = 0;
	
	private int maxBalls = 30;
	
	public int myBalls = 0;
	
	private boolean rotating = false;
	
	private int rotation = 0;
	
	private int maxHit = 30;
	
	private final int maxDistance = 20;
	
	private int totalRotations = 0;
	
	private boolean justClicked = false;

	public static void makeBall(Player player)
	{
		//An interface could be added instead of making all.
		if (!player.getItemAssistant().playerHasItem(2353) || !player.getItemAssistant().playerHasItem(4))
		{
			player.getPacketSender().sendMessage("You need an ammo mould and steel bars to make cannonballs.");
			return;
		}
		if (!player.isSmithing)
		{
			player.isSmithing = true;
			CycleEventHandler.getSingleton().addEvent(player, new CycleEvent() {
				@Override
				public void execute(CycleEventContainer container) {
					if (player.isWoodcutting || player.isCrafting || player.isFletching || player.isMoving || player.isMining || player.isBusy || player.isShopping || player.isFiremaking || player.isSpinning || player.isPotionMaking || player.playerIsFishing || player.isBanking || player.isSmelting || player.isTeleporting || player.isHarvesting || player.playerIsCooking || player.isPotCrafting ||!player.isSmithing || !player.getItemAssistant().playerHasItem(2353) || !player.getItemAssistant().playerHasItem(4))
					{
						container.stop();
						return;
					}
					else
					{
						player.startAnimation(899);
						player.getItemAssistant().deleteItem(2353, 1);
						player.getItemAssistant().addItem(2, 4);
						player.getPacketSender().sendMessage("You make some cannonballs.");
						player.getPlayerAssistant().addSkillXP(26, GameConstants.SMITHING);
						player.getPacketSender().sendSound(352, 100, 0);
					}
				}

				@Override
				public void stop() {
					player.isSmithing = false;
				}
			}, 3);
		}

	}
	public void placeCannon() {
		if (settingUp) {
			return;
		}
		if (noSetUpArea()) {
			player.getPacketSender().sendMessage("You are not allowed to set up a cannon here!");
			return;
		}
		if (hasCannon()) {
			player.getPacketSender().sendMessage("You already have a cannon placed!");
			return;
		}
		if (nearCannon()) {
			player.getPacketSender().sendMessage("You must be farther away from an existing cannon to set a new one up!");
			return;
		}
		if (!canSetUp()) {
			player.getPacketSender().sendMessage("You need all the parts of the cannon to set a cannon up.");
			return;
		}
		for (int i = 0; i < 50; i++) {
			if (GameEngine.cannonsX[i] == 0 && GameEngine.cannonsY[i] == 0) {
				GameEngine.cannonsX[i] = player.absX;
				GameEngine.cannonsY[i] = player.absY;
				break;
			}
		}
		CycleEventHandler.getSingleton().addEvent(player, new CycleEvent() {
		@Override
    		public void execute(CycleEventContainer container) {
				if (setUpStage >= 4) {
					container.stop();
					setUpStage = 0;
					player.CannonSetupStage = setUpStage;
					settingUp = false;
					return;
				}
				if (!canSetUp()) {
					player.getPacketSender().sendMessage("You need all the parts of the cannon to set a cannon up.");
					container.stop();
					return;
				}
				settingUp = true;
				player.startAnimation(827);
				player.turnPlayerTo(player.absX, player.absY);
				player.cannonX = player.absX;
				player.cannonY = player.absY;
				placeObject(OBJECT_PARTS[setUpStage], player.absX, player.absY, true);
				player.getItemAssistant().deleteItem(ITEM_PARTS[setUpStage], 1);
				setUpStage ++;
				player.CannonSetupStage = setUpStage;
			}
			@Override
			public void stop() {
				/**
				 * Balls
				 */
				int cballs = getBalls();
				int amount = cballs - myBalls;
				player.getItemAssistant().deleteItem(ballsID, player.getItemAssistant().getItemSlot(ballsID), amount);
				myBalls = cballs;
			}
		}, 2);
	}
		
		public void loginCheck() {
			if (player.lostCannon) {
				player.getPacketSender().sendMessage("@red@You can collect your cannon from Nulodion.");
			}
		}
		private boolean canSetUp() {
			if (setUpStage == 0) {
				if (player.getItemAssistant().playerHasItem(ITEM_PARTS[0])  && player.getItemAssistant().playerHasItem(ITEM_PARTS[1]) && player.getItemAssistant().playerHasItem(ITEM_PARTS[2]) && player.getItemAssistant().playerHasItem(ITEM_PARTS[3])) {
					return true;
				}
			} else if (setUpStage == 1) {
				if (player.getItemAssistant().playerHasItem(ITEM_PARTS[1]) && player.getItemAssistant().playerHasItem(ITEM_PARTS[2]) && player.getItemAssistant().playerHasItem(ITEM_PARTS[3])) {
					return true;
				}
			} else if (setUpStage == 2) {
				if (player.getItemAssistant().playerHasItem(ITEM_PARTS[2]) && player.getItemAssistant().playerHasItem(ITEM_PARTS[3])) {
					return true;
				}
			} else if (setUpStage == 3) {
				if (player.getItemAssistant().playerHasItem(ITEM_PARTS[3])) {
					return true;
				}
			}
			return false;
		}
		
		public boolean hasCannon() {
			return (player.CannonSetupStage != 0 || (player.cannonX > 0 && player.cannonY > 0));
		}
		
		private boolean myCannon(int x, int y) {
			return (player.cannonX == x) && (player.cannonY == y);
		}
		
		public int getBalls() {
			int cannonBalls = player.getItemAssistant().getItemAmount(ballsID);
			if (cannonBalls >= maxBalls) {
				return maxBalls;
			}
			return cannonBalls;
		}
		
		public boolean nearCannon() {
			for(int i = 0; i < GameEngine.cannonsX.length; i++) {
				if ((player.absX >= GameEngine.cannonsX[i] - 1) && (player.absX <= GameEngine.cannonsX[i] + 1) && (player.absY >= GameEngine.cannonsY[i] - 2) && (player.absY <= GameEngine.cannonsY[i] + 1)) {
						return true;
					}
				}
			return false;
		}
		
		public void loadCannons() {
			for(int i = 0; i < GameEngine.cannonsX.length; i++) {
				if (GameEngine.cannonsX[i] != 0) {
					player.getPacketSender().checkObjectSpawn(6, GameEngine.cannonsX[i], GameEngine.cannonsY[i], 0, 10);
				}
			}
		}
		
		public void loadCannon(int x, int y) {
			int cballs = getBalls();
			if (!myCannon(x, y)) {
				player.getPacketSender().sendMessage("You can't load somebody else's cannon!");
				return;
			}
			if (myBalls <= 29) {
				int amount = cballs - myBalls;
				player.getItemAssistant().deleteItem(ballsID, player.getItemAssistant().getItemSlot(ballsID), amount);
				myBalls = cballs;
				if (player.getItemAssistant().playerHasItem(ballsID)) {
					player.getPacketSender().sendMessage(amount > 1 ? "You load the cannon with " + amount + " cannonballs." : "You load the cannon with 1 cannonball.");
				} else {
					player.getPacketSender().sendMessage("You have no cannonballs to load into the cannon.");
				}
			}
		}
		
		public void clickCannon(int x, int y) {
			if (!myCannon(x, y)) {
				player.getPacketSender().sendMessage("You can't fire somebody else's cannon!");
				return;
			}
			if (myBalls == 0) {
				player.getPacketSender().sendMessage("Your cannon has run out of cannonballs.");
				return;
			}
			if (myBalls >= 1 && rotating == false) {
				shoot();
			} else if (myBalls >= 1 && rotating) {
				player.getPacketSender().sendMessage("Your cannon is already shooting.");
			}
		}
			
		public void handleDisconnect() {
			removeObject(player.cannonX, player.cannonY);
			for(int i = 0; i < GameEngine.cannonsX.length; i++) {
				if (GameEngine.cannonsX[i] == player.cannonX && GameEngine.cannonsY[i] == player.cannonY) {
					GameEngine.cannonsX[i] = 0;
					GameEngine.cannonsY[i] = 0;
					GameEngine.cannonsO[i] = null;
				}
			}
		}
		
		public void handleDeath() {
			if (hasCannon()) {
				player.lostCannon = true;
				removeObject(player.cannonX, player.cannonY);
				for(int i = 0; i < GameEngine.cannonsX.length; i++) {
					if (GameEngine.cannonsX[i] == player.cannonX && GameEngine.cannonsY[i] == player.cannonY) {
						GameEngine.cannonsX[i] = 0;
						GameEngine.cannonsY[i] = 0;
						GameEngine.cannonsO[i] = null;
					}
				}
				player.cannonX = 0;
				player.cannonY = 0;
			}
		}
		
		public void shoot() {
			if (justClicked) {
				return;
			}
			rotating = true;
			justClicked = true;
			CycleEventHandler.getSingleton().addEvent(player, new CycleEvent() {
				@Override
				public void execute(CycleEventContainer e) {
					totalRotations += 1;
					if (totalRotations >= 2) {
						justClicked = false;
					}
					if(rotating == false) {
						e.stop();
					}
					if (myBalls < 1) {
						rotating = false;
						e.stop();
					}
					if (rotating) {
						rotation += 1;
						rotateCannon();
						shootNpcs();
					}
				}
				@Override
				public void stop() {
					
				}
			}, 2);
		}
		
		private void rotateCannon() {
		switch (rotation) {
			case 1: //north
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 516, 10, -1);
			break;
			case 2: //north-east
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 517, 10, -1);
			break;
			case 3: //east
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 518, 10, -1);
			break;
			case 4: //south-east
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 519, 10, -1);
			break;
			case 5: //south
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 520, 10, -1);
			break;
			case 6: //south-west
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 521, 10, -1);
			break;
			case 7: //west
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 514, 10, -1);
			break;
			case 8: //north-west
				player.getPlayerAssistant().objectAnim(player.cannonX, player.cannonY, 515, 10, -1);
				rotation = 0;
			break;
			}
		}
		
		public void removeCannon(int x, int y) {
			for (int i = 0; i < GameEngine.cannonsX.length; i++) {
				if (GameEngine.cannonsX[i] == x && GameEngine.cannonsY[i] == y) {
					GameEngine.cannonsX[i] = 0;
					GameEngine.cannonsY[i] = 0;
					break;
				}
			}
		}

		public void pickup(int x, int y) {
			if (!myCannon(x, y)) {
				player.getPacketSender().sendMessage("You can't pick up somebody else's cannon!");
				return;
			}
			if (rotating) {
				rotating = false;
			}
			if (player.getItemAssistant().freeSlots() > 3) {
				player.startAnimation(827);
				player.getPacketSender().sendMessage("You pick up the cannon. It's really heavy.");
				removeCannon(player.cannonX, player.cannonY);
				player.getItemAssistant().addItem(ITEM_PARTS[0], 1);
				player.getItemAssistant().addItem(ITEM_PARTS[1], 1);
				player.getItemAssistant().addItem(ITEM_PARTS[2], 1);
				player.getItemAssistant().addItem(ITEM_PARTS[3], 1);
			} else {
				player.getPacketSender().sendMessage("You don't have enough free inventory slots to do that.");
			}
			if (myBalls > 0) {
				player.getItemAssistant().addItem(ballsID, myBalls);
				myBalls = 0;
			}
			removeObject(player.cannonX, player.cannonY);
			player.cannonX = 0;
			player.cannonY = 0;
			player.cannonX = 0;
			player.cannonY = 0;
		}

		public void placeObject(int id, int x, int y, boolean add) {
			GameEngine.objectHandler.placeObject(new Objects(id, x, y, 0, 516, 10, 0));
			if (add)
			Region.addObject(id, x, y, 0, 10, 516, true);
		}

		public void removeObject(int x, int y) {
			placeObject(-1, x, y, false);
		}
		
		public boolean noSetUpArea() {
			return Boundary.isIn(player, Boundary.BANK_AREA) || player.inFightCaves();
		}
		
		private int getHit() {
			int hits = Misc.random(2);
			switch (hits) {
			case 0:
				return Misc.random(maxHit);
			case 1:
				return 15+Misc.random(maxHit-15);
			case 2:
				return 10+Misc.random(maxHit-10);
			}
			return 0;
		}
		
		public void shootNpcs() {
			int damage = getHit();
			Npc target = targetNpc();
			if (target != null) {
				if (damage > target.HP) {
					damage = target.HP;
				}
				if (!Boundary.isIn(player, Boundary.MULTI)) {
					if (target.underAttackBy > 0 && target.underAttackBy != player.playerId) {
						return;
					}
					if (player.underAttackBy2 > 0 && player.underAttackBy2 != target.npcId) {
						return;
					}
				}
				cannonProjectile(target);
				target.hitDiff2 = damage;
				target.HP -= damage;
				player.globalDamageDealt += damage;
				target.killerId = player.playerId;
				target.killedBy = player.playerId;
				target.facePlayer(player.playerId);
				target.hitUpdateRequired2 = true;
				target.updateRequired = true;
				myBalls -= 1;
				player.getPlayerAssistant().addSkillXP(damage * CombatConstants.RANGE_EXP_RATE, GameConstants.RANGED);
			}
		}
		
		
		private Npc targetNpc() {
			for (int i = 0; i < NpcHandler.MAX_NPCS; i++) {
				Npc npc = NpcHandler.npcs[i];
				if (npc == null || npc.heightLevel != player.heightLevel || !canAttackSlayer(i)) {
					continue;
				}
				int myX = player.cannonX;
				int myY = player.cannonY;
				int theirX = npc.absX;
				int theirY = npc.absY;



				if (!npc.isDead && !npc.isDead && npc.HP != 0 && npc.npcType != 1266 && npc.npcType != 1268 && inDistance(theirX, theirY)) {
					switch (rotation) {
						case 1:
							if (theirY > myY && theirX >= myX - 1 && theirX <= myX + 1) {
								return npc;
							}
							break;
						case 2:
							if (theirX >= myX + 1 && theirY >= myY + 1) {
								return npc;
							}
							break;
						case 3:
							if (theirX > myX && theirY >= myY - 1 && theirY <= myY + 1) {
								return npc;
							}
							break;
						case 4:
							if (theirY <= myY - 1 && theirX >= myX + 1) {
								return npc;
							}
							break;
						case 5:
							if (theirY < myY && theirX >= myX - 1 && theirX <= myX + 1) {
								return npc;
							}
							break;
						case 6:
							if (theirX <= myX - 1 && theirY <= myY - 1) {
								return npc;
							}
							break;
						case 7:
							if (theirX < myX && theirY >= myY - 1 && theirY <= myY + 1) {
								return npc;
							}
							break;
						case 8:
							if (theirX <= myX - 1 && theirY >= myY + 1) {
								return npc;
							}
							break;
                	}
            	}
			}
			return null;
		}
		
		public boolean inDistance(int npcX, int npcY) {
			return (npcX >= player.cannonX - maxDistance && npcX <= player.cannonX + maxDistance && npcY >= player.cannonY - maxDistance && npcY <= player.cannonY + maxDistance);
		}

		public boolean canAttackSlayer(int i){
			return player.playerLevel[GameConstants.SLAYER] >= player.getSlayer().getRequiredLevel(NpcHandler.npcs[i].npcType);
		}
		
		private void cannonProjectile(Npc n) {
			int oX = player.cannonX+getShootXPos();
			int oY = player.cannonY+getShootYPos();
			int offX = ((oX - n.absX) * -1);
			int offY = ((oY - n.absY) * -1);
        	player.getPlayerAssistant().createPlayersProjectile(oX, oY, offY, offX, 50, 60, 53, 20, 20, -player.oldNpcIndex + 1, 30);
		}
	
		public int getShootXPos() {
			switch(rotation) {
				case 1:
					return 1;
				case 2:
					return 2;
				case 3:
					return 2;
				case 4:
					return 2;
				case 5:
					return 1;
				case 6:
					return 0;
				case 7:
					return 0;
				case 8:
					return 0;
			}
			return 0;
		}
		
		public int getShootYPos() {
			switch(rotation) {
				case 1:
					return 2;
				case 2:
					return 2;
				case 3:
					return 1;
				case 4:
					return 0;
				case 5:
					return 0;
				case 6:
					return 0;
				case 7:
					return 1;
				case 8:
					return 2;
			}
			return 0;
		}
}