package com.rs.game.map.bossInstance;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import com.rs.game.WorldTile;
import com.rs.game.map.MapInstance;
import com.rs.game.map.bossInstance.BossInstanceHandler.Boss;
import com.rs.game.player.Player;

public abstract class BossInstance {

	/*
	 * spawn speed
	 */
	public static final int STANDARD = 1, FAST = 2, FASTEST = 4;
	public static final int FFA = 0, PIN = 1, FRIENDS_ONLY = 2;

	public static int STARTING = 0, RUNNING = 1, FINISHED = 2;

	//null if real world
	private MapInstance map;
	private Player owner;
	private List<Player> players;
	private InstanceSettings settings;
	private int stage;

	/*
	 * creates instance
	 */
	public BossInstance(Player owner, InstanceSettings settings) {
		this.owner = owner;
		this.settings = settings;
		players = new CopyOnWriteArrayList<Player>();
		if (!isPublic())
			owner.setLastBossInstanceSettings(settings);
		init();
	}

	public InstanceSettings getSettings() {
		return settings;
	}

	public int getPlayersCount() {
		return players.size();
	}

	public void init() {
		if (owner != null) {
			owner.lock();
			int[] pos = getMapPos();
			int[] size = getMapSize();
			setMapInstance(pos[0], pos[1], size[0], size[1]);
			map.load(new Runnable() {
				@Override
				public void run() {
					loadMapInstance();
					stage = RUNNING;
					enterInstance(owner, false);
				}
			});
		}
		else {
			loadMapInstance();
			stage = RUNNING;
		}
	}

	public boolean isInstanceReady() {
		return stage == RUNNING;
	}

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

	public boolean isPlayerInside(Player player) {
		synchronized (BossInstanceHandler.LOCK) {
			return players.contains(player);
		}
	}

	public abstract int[] getMapPos();

	//return 1,1 by default
	public abstract int[] getMapSize();

	public void setMapInstance(int chunkX, int chunkY, int ratioX, int ratioY) {
		map = new MapInstance(chunkX, chunkY, ratioX, ratioY);
	}

	public abstract void loadMapInstance();

	public void enterInstance(Player player, boolean login) {
		synchronized (BossInstanceHandler.LOCK) {
			if (!login)
				player.useStairs(-1, getTile(settings.getBoss().getInsideTile()), 0, 2);
			players.add(player);
			playMusic(player);
			if (!isPublic())
				player.getPackets().sendGameMessage("Welcome to this session agaisnt <col=00FFFF>" + getInstanceName() + "</col>. This arena will expire in " + (settings.getTimeRemaining() / 60000) + " minutes.");
			if (!login)
				player.getControlerManager().startControler(settings.getBoss().getControllerName(), this);
			player.setLastBossInstanceKey(owner == null ? null : owner.getUsername());
		}
	}

	public void playMusic(Player player) {
		player.getMusicsManager().forcePlayMusic(settings.getBoss().getMusicId());
	}

	public String getInstanceName() {
		return settings.getBoss().name().replace("_", " ");
	}

	public static final int TELEPORTED = 0, LOGGED_OUT = 1, EXITED = 2,
			DIED = 3;

	public void leaveInstance(Player player, int type) {
		synchronized (BossInstanceHandler.LOCK) {
			if (type == EXITED)
				player.useStairs(-1, settings.getBoss().getOutsideTile(), 0, 2);
			else if (type == LOGGED_OUT && !isPublic()) //if public no need to move the player to entrance :p(exept for instance vorago but thats done at vorago instance extend)
				player.setLocation(settings.getBoss().getOutsideTile());
			player.getMusicsManager().reset();
			players.remove(player);
			if (players.isEmpty()) //public version
				finish();
		}
	}

	public void finish() {
		if (!isPublic()) {
			stage = FINISHED;
			map.destroy(null);
			settings.getBoss().getCachedInstances().remove(getOwnerUsername());
		}
	}

	public WorldTile getTile(WorldTile tile) {
		return getTile(tile.getX(), tile.getY(), tile.getPlane());
	}

	//gets the instanced coords of the world tile(notice if the area instanced differs from coords it will give wrong tile, the ratio part is to make sure the coords are inside map
	public WorldTile getTile(int x, int y, int plane) {
		WorldTile tile;
		if (owner != null) {
			int[] originalPos = map.getOriginalPos();
			tile = map.getTile((x - originalPos[0] * 8) % (map.getRatioX() * 64), (y - originalPos[1] * 8) % (map.getRatioY() * 64));
			tile.moveLocation(0, 0, plane);
		}
		else
			tile = new WorldTile(x, y, plane);
		return tile;
	}

	public String getOwnerUsername() {
		return owner == null ? "" : owner.getUsername();
	}

	public boolean isPublic() {
		return owner == null;
	}

	public Boss getBoss() {
		return settings.getBoss();
	}

	public boolean isFinished() {
		return stage == FINISHED;
	}

}
