package com.rs.game.minigames.pest;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

import com.rs.Settings;
import com.rs.cache.loaders.NPCDefinitions;
import com.rs.executor.GameExecutorManager;
import com.rs.game.World;
import com.rs.game.WorldTile;
import com.rs.game.map.MapBuilder;
import com.rs.game.npc.NPC;
import com.rs.game.npc.pest.PestMonsters;
import com.rs.game.npc.pest.PestPortal;
import com.rs.game.npc.pest.Shifter;
import com.rs.game.npc.pest.Spinner;
import com.rs.game.npc.pest.Splatter;
import com.rs.game.player.Player;
import com.rs.game.player.controllers.pestcontrol.PestControlGame;
import com.rs.game.tasks.WorldTask;
import com.rs.game.tasks.WorldTasksManager;
import com.rs.utils.Logger;
import com.rs.utils.Utils;

public class PestControl {

    private final static int[][] PORTAL_LOCATIONS = { { 4, 56, 45, 21, 32 }, { 31, 28, 10, 9, 32 } };
    private final static int[] KNIGHT_IDS = { 3782, 3784, 3785 };

    private int[] boundChunks;
    private int[] pestCounts = new int[5];

    private List<Player> team;
    private List<NPC> brawlers = new LinkedList<NPC>();
    private PestPortal[] portals = new PestPortal[4];

    private PestPortal knight;
    private PestData data;

    private byte lockedPortals = 5;

    private class PestGameTimer extends TimerTask {

	int seconds = 1200;

	@Override
	public void run() {
	    try {
		updateTime(seconds / 60);
		if (seconds == 0 || canFinish()) {
		    endGame();
		    cancel();
		    return;
		}
		if (seconds % 10 == 0)
		    sendPortalInterfaces();
		if (seconds % 20 == 0)
		    unlockPortal();
		seconds--;
	    } catch (Throwable e) {
		Logger.handle(e);
	    }
	}
    }

    public PestControl(List<Player> team, PestData data) {
	this.team = Collections.synchronizedList(team);
	this.data = data;
    }

    public PestControl create() {
	final PestControl instance = this;
	GameExecutorManager.slowExecutor.execute(new Runnable() {
	    @Override
	    public void run() {
		try {
		    boundChunks = MapBuilder.findEmptyChunkBound(8, 8);
		    MapBuilder.copyAllPlanesMap(328, 320, boundChunks[0], boundChunks[1], 8);
		    sendBeginningWave();
		    unlockPortal();
		    for (Player player : team) {
			player.getControlerManager().removeControlerWithoutCheck();
			player.useStairs(-1, getWorldTile(35 - Utils.random(4), 54 - (Utils.random(3))), 1, 2);
			player.getControlerManager().startControler("PestControlGame", instance);
		    }
		    GameExecutorManager.fastExecutor.schedule(new PestGameTimer(), 1000, 1000);
		} catch (Throwable e) {
		    Logger.handle(e);
		}
	    }
	});
	return instance;
    }

    private void sendBeginningWave() {
	knight = new PestPortal(KNIGHT_IDS[Utils.random(KNIGHT_IDS.length)], true, getWorldTile(32, 32), this);
	knight.unlock();
	for (int index = 0; index < portals.length; index++) {
	    PestPortal portal = portals[index] = new PestPortal(6146 + index, true,
		    getWorldTile(PORTAL_LOCATIONS[0][index], PORTAL_LOCATIONS[1][index]), this);
	    portal.setHitpoints(data.ordinal() == 0 ? 2000 : 2500);
	}
    }

    public boolean createPestNPC(int index) {
	if (pestCounts[index] >= (index == 4 ? 4 : (portals[index] != null && portals[index].isLocked()) ? 5 : 15))
	    return false;
	pestCounts[index]++;
	WorldTile baseTile = getWorldTile(PORTAL_LOCATIONS[0][index], PORTAL_LOCATIONS[1][index]);
	WorldTile teleTile = baseTile;
	int npcId = index == 4 ? data.getShifters()[Utils.random(data.getShifters().length)]
		: data.getPests()[Utils.random(data.getPests().length)];
	NPCDefinitions defs = NPCDefinitions.getNPCDefinitions(npcId);
	for (int trycount = 0; trycount < 10; trycount++) {
	    teleTile = new WorldTile(baseTile, 5);
	    if (World.isTileFree(baseTile.getPlane(), teleTile.getX(), teleTile.getY(), defs.size))
		break;
	    teleTile = baseTile;
	}
	String name = defs.getName().toLowerCase();
	if (name.contains("shifter"))
	    new Shifter(npcId, teleTile, -1, true, true, index, this);
	else if (name.contains("splatter"))
	    new Splatter(npcId, teleTile, -1, true, true, index, this);
	else if (name.contains("spinner"))
	    new Spinner(npcId, teleTile, -1, true, true, index, this);
	else if (name.contains("brawler"))
	    brawlers.add(new PestMonsters(npcId, teleTile, -1, true, true, index, this));
	else
	    new PestMonsters(npcId, teleTile, -1, true, true, index, this);
	return true;
    }

    public void endGame() {
	final List<Player> team = new LinkedList<Player>();
	team.addAll(this.team);
	this.team.clear();
	for (final Player player : team) {
	    final int zeal = (int) ((PestControlGame) player.getControlerManager().getControler()).getPoints();
	    player.getControlerManager().forceStop();
	    WorldTasksManager.schedule(new WorldTask() {
		@Override
		public void run() {
		    distributeReward(player, zeal);
		}
	    }, 1);
	}
	/*
	 * 6sec is a waste of time lo
	 */
	GameExecutorManager.slowExecutor.schedule(new Runnable() {
	    @Override
	    public void run() {
		MapBuilder.destroyMap(boundChunks[0], boundChunks[1], 8, 8);
		boundChunks = null;
	    }
	}, 1200, TimeUnit.MILLISECONDS);
    }

    private void distributeReward(Player player, int knightZeal) {
	if (knight.isDead())
	    player.getDialogueManager().startDialogue("SimpleMessage",
		    "You failed to protect the void knight and have not been awarded any points.");
	else if (knightZeal < 5000)
	    player.getDialogueManager().startDialogue("SimpleMessage",
		    "The knights notice your lack of zeal in that battle and have not presented you with any points.");
	else {
	    int commendation = data.getReward() * (Settings.getDropQuantityRate() + 1);
	    player.getDialogueManager().startDialogue("SimpleMessage",
		    "Congradulations! You have successfully kept the lander safe and have been awarded: "
	    + commendation + " commendation points.");
	    player.setCommendation(player.getCommendation() + commendation);
	}
    }

    private void sendPortalInterfaces() {
	for (Player player : team) {
	    for (int count = 0; count < portals.length; count++) {
		PestPortal npc = portals[count];
		if (npc != null) {
		    player.getPackets().sendIComponentText(408, count + 9, npc.getHitpoints() + "");
		    if (npc.isDead()) {
			player.getPackets().sendHideIComponent(408, count + 25, false);
			player.getPackets().sendHideIComponent(408, count+20, true);
		    }
		}
	    }
	    player.getPackets().sendIComponentText(408, 5, "" + knight.getHitpoints());
	}
    }

    public void unlockPortal() {
	do {
	    int count = Utils.random(portals.length);
	    PestPortal portal = portals[count];
	    if (portal == null || portal.isDead())
		continue;
	    portal.unlock();
	} while (true);
    }

    public boolean isBrawlerAt(WorldTile tile) {
	for (Iterator<NPC> it = brawlers.iterator(); it.hasNext();) {
	    NPC npc = it.next();
	    if (npc.isDead() || npc.hasFinished()) {
		it.remove();
		continue;
	    }
	    if (npc.getX() == tile.getX() && npc.getY() == tile.getY() && tile.getPlane() == tile.getPlane())
		return true;
	}
	return false;
    }

    private void updateTime(int minutes) {
	for (Player player : team)
	    player.getPackets().sendIComponentText(408, 4, minutes + " min");
    }

    public void sendTeamMessage(String message) {
	for (Player player : team)
	    player.getPackets().sendGameMessage(message, true);
    }

    private boolean canFinish() {
	if (knight == null || knight.isDead())
	    return true;
	return lockedPortals == 0;
    }

    public WorldTile getWorldTile(int mapX, int mapY) {
	if (boundChunks == null) // temporary fix..
	    return Settings.START_PLAYER_LOCATION;
	return new WorldTile(boundChunks[0] * 8 + mapX, boundChunks[1] * 8 + mapY, 0);
    }

    public PestPortal[] getPortals() {
	return portals;
    }

    public List<Player> getPlayers() {
	return team;
    }

    public NPC getKnight() {
	return knight;
    }

    public enum PestData {

	NOVICE(new int[] { /* Shifters */
		3732, 3733, 3734, 3735, /* Ravagers */
		3742, 3743, 3744, /* Brawler */
		3772, 3773, /* Splatter */
		3727, 3728, 3729, /* Spinner */
		3747, 3748, 3749, /* Torcher */
		3752, 3753, 3754, 3755, /* Defiler */
		3762, 3763, 3764, 3765 }, new int[] { 3732, 3733, 3734, 3735 }, 2),

	INTERMEDIATE(new int[] { /* Shifters */
		3734, 3735, 3736, 3737, 3738, 3739/* Ravagers */, 3744, 3743, 3745, /* Brawler */
		3773, 3775, 3776, /* Splatter */
		3728, 3729, 3730, /* Spinner */
		3748, 3749, 3750, 3751, /* Torcher */
		3754, 3755, 3756, 3757, 3758, 3759, /* Defiler */
		3764, 3765, 3766, 3768, 3769 }, new int[] { 3734, 3735, 3736, 3737, 3738, 3739 }, 3),

	VETERAN(new int[] { /* Shifters */
		3736, 3737, 3738, 3739, 3740, 3741 /* Ravagers */, 3744, 3745, 3746, /* Brawler */
		3776, 3774, /* Splatter */
		3729, 3730, 3731, /* Spinner */
		3749, 3750, 3751, /* Torcher */
		3758, 3759, 3760, 3761, /* Defiler */
		3770, 3771 }, new int[] { 3736, 3737, 3738, 3739, 3740, 3741 }, 4);

	private int[] waves, shifters;
	private int reward;

	private PestData(int[] pests, int[] shifters, int reward) {
	    this.waves = pests;
	    this.shifters = shifters;
	    this.reward = reward;
	}

	public int[] getShifters() {
	    return shifters;
	}

	public int[] getPests() {
	    return waves;
	}

	public int getReward() {
	    return reward;
	}
    }

    public int[] getPestCounts() {
	return pestCounts;
    }

    public PestData getPestData() {
	return data;
    }

    public int getPortalCount() {
	return lockedPortals;
    }
}
