package cn.citycraft.AuthMe.process.join;

import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.scheduler.BukkitTask;

import cn.citycraft.AuthMe.AuthMe;
import cn.citycraft.AuthMe.ConsoleLogger;
import cn.citycraft.AuthMe.Utils;
import cn.citycraft.AuthMe.Utils.GroupType;
import cn.citycraft.AuthMe.cache.auth.PlayerAuth;
import cn.citycraft.AuthMe.cache.auth.PlayerCache;
import cn.citycraft.AuthMe.cache.limbo.LimboCache;
import cn.citycraft.AuthMe.datasource.DataSource;
import cn.citycraft.AuthMe.listener.AuthMePlayerListener;
import cn.citycraft.AuthMe.settings.Messages;
import cn.citycraft.AuthMe.settings.Settings;
import cn.citycraft.AuthMe.settings.Spawn;
import cn.citycraft.AuthMe.task.MessageTask;
import cn.citycraft.AuthMe.task.TimeoutTask;
import fr.xephi.authme.events.FirstSpawnTeleportEvent;
import fr.xephi.authme.events.ProtectInventoryEvent;
import fr.xephi.authme.events.SpawnTeleportEvent;

public class AsyncronousJoin {

	private final Messages m = Messages.getInstance();
	protected DataSource database;
	protected String name;
	protected Player player;
	protected AuthMe plugin;

	public AsyncronousJoin(final Player player, final AuthMe plugin, final DataSource database) {
		this.player = player;
		this.plugin = plugin;
		this.database = database;
		this.name = player.getName().toLowerCase();
	}

	public void process() {
		if (AuthMePlayerListener.gameMode.containsKey(name)) {
			AuthMePlayerListener.gameMode.remove(name);
		}
		AuthMePlayerListener.gameMode.putIfAbsent(name, player.getGameMode());
		final BukkitScheduler sched = plugin.getServer().getScheduler();

		if (Utils.isNPC(player) || Utils.isUnrestricted(player)) {
			return;
		}

		if (plugin.ess != null && Settings.disableSocialSpy) {
			plugin.ess.getUser(player).setSocialSpyEnabled(false);
		}

		final String ip = plugin.getIP(player);
		if (Settings.isAllowRestrictedIp && !Settings.getRestrictedIp(name, ip)) {
			final GameMode gM = AuthMePlayerListener.gameMode.get(name);
			sched.scheduleSyncDelayedTask(plugin, new Runnable() {

				@Override
				public void run() {
					AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
					player.setGameMode(gM);
					player.kickPlayer("You are not the Owner of this account, please try another name!");
					if (Settings.banUnsafeIp) {
						plugin.getServer().banIP(ip);
					}
				}

			});
			return;
		}
		if (Settings.getMaxJoinPerIp > 0 && !plugin.authmePermissible(player, "authme.allow2accounts") && !ip.equalsIgnoreCase("127.0.0.1") && !ip.equalsIgnoreCase("localhost")) {
			if (plugin.hasJoinedIp(player.getName(), ip)) {
				sched.scheduleSyncDelayedTask(plugin, new Runnable() {

					@Override
					public void run() {
						player.kickPlayer("§c同一个IP只允许一个玩家进入!");
					}

				});
				return;
			}
		}
		final Location spawnLoc = plugin.getSpawnLocation(player);
		final boolean isAuthAvailable = database.isAuthAvailable(name);
		if (isAuthAvailable) {
			if (Settings.isForceSurvivalModeEnabled && !Settings.forceOnlyAfterLogin) {
				sched.scheduleSyncDelayedTask(plugin, new Runnable() {

					@Override
					public void run() {
						AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
						Utils.forceGM(player);
					}

				});
			}
			if (!Settings.noTeleport) {
				if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) {
					sched.scheduleSyncDelayedTask(plugin, new Runnable() {

						@Override
						public void run() {
							final SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name));
							plugin.getServer().getPluginManager().callEvent(tpEvent);
							if (!tpEvent.isCancelled()) {
								if (player.isOnline() && tpEvent.getTo() != null) {
									if (tpEvent.getTo().getWorld() != null) {
										player.teleport(tpEvent.getTo());
									}
								}
							}
						}

					});
				}
			}
			placePlayerSafely(player, spawnLoc);
			LimboCache.getInstance().updateLimboPlayer(player);
			// protect inventory
			if (Settings.protectInventoryBeforeLogInEnabled && plugin.inventoryProtector != null) {
				final ProtectInventoryEvent ev = new ProtectInventoryEvent(player);
				plugin.getServer().getPluginManager().callEvent(ev);
				if (ev.isCancelled()) {
					plugin.inventoryProtector.sendInventoryPacket(player);
					if (!Settings.noConsoleSpam) {
						ConsoleLogger.info("玩家 " + player.getName() + " 的背包保护事件已被取消...");
					}
				}
			}
		} else {
			if (Settings.isForceSurvivalModeEnabled && !Settings.forceOnlyAfterLogin) {
				sched.scheduleSyncDelayedTask(plugin, new Runnable() {

					@Override
					public void run() {
						AuthMePlayerListener.causeByAuthMe.putIfAbsent(name, true);
						Utils.forceGM(player);
					}

				});
			}
			if (!Settings.unRegisteredGroup.isEmpty()) {
				Utils.setGroup(player, Utils.GroupType.UNREGISTERED);
			}
			if (!Settings.isForcedRegistrationEnabled) {
				return;
			}
			if (!Settings.noTeleport) {
				if (!needFirstspawn() && Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) {
					sched.scheduleSyncDelayedTask(plugin, new Runnable() {
						@Override
						public void run() {
							final SpawnTeleportEvent tpEvent = new SpawnTeleportEvent(player, player.getLocation(), spawnLoc, PlayerCache.getInstance().isAuthenticated(name));
							plugin.getServer().getPluginManager().callEvent(tpEvent);
							if (!tpEvent.isCancelled()) {
								if (player.isOnline() && tpEvent.getTo() != null) {
									if (tpEvent.getTo().getWorld() != null) {
										player.teleport(tpEvent.getTo());
									}
								}
							}
						}

					});
				}
			}

		}
		String[] msg;
		if (Settings.emailRegistration) {
			msg = isAuthAvailable ? m.send("login_msg") : m.send("reg_email_msg");
		} else {
			msg = isAuthAvailable ? m.send("login_msg") : m.send("reg_msg");
		}
		final int time = Settings.getRegistrationTimeout * 20;
		final int msgInterval = Settings.getWarnMessageInterval;
		if (time != 0) {
			final BukkitTask id = sched.runTaskLaterAsynchronously(plugin, new TimeoutTask(plugin, name, player), time);
			if (!LimboCache.getInstance().hasLimboPlayer(name)) {
				LimboCache.getInstance().addLimboPlayer(player);
			}
			LimboCache.getInstance().getLimboPlayer(name).setTimeoutTaskId(id);
		}
		if (!LimboCache.getInstance().hasLimboPlayer(name)) {
			LimboCache.getInstance().addLimboPlayer(player);
		}
		if (isAuthAvailable) {
			Utils.setGroup(player, GroupType.NOTLOGGEDIN);
		} else {
			Utils.setGroup(player, GroupType.UNREGISTERED);
		}
		sched.scheduleSyncDelayedTask(plugin, new Runnable() {

			@Override
			public void run() {
				if (player.isOp()) {
					player.setOp(false);
				}
				if (player.getGameMode() != GameMode.CREATIVE && !Settings.isMovementAllowed) {
					player.setAllowFlight(true);
					player.setFlying(true);
				}
				player.setNoDamageTicks(Settings.getRegistrationTimeout * 20);
				if (Settings.useEssentialsMotd) {
					player.performCommand("motd");
				}
				if (Settings.applyBlindEffect) {
					player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, Settings.getRegistrationTimeout * 20, 2));
				}
				if (!Settings.isMovementAllowed && Settings.isRemoveSpeedEnabled) {
					player.setWalkSpeed(0.0f);
					player.setFlySpeed(0.0f);
				}
			}

		});
		if (Settings.isSessionsEnabled && isAuthAvailable && (PlayerCache.getInstance().isAuthenticated(name) || database.isLogged(name))) {
			if (plugin.sessions.containsKey(name)) {
				plugin.sessions.get(name).cancel();
			}
			plugin.sessions.remove(name);
			final PlayerAuth auth = database.getAuth(name);
			if (auth != null && auth.getIp().equals(ip)) {
				m.send(player, "valid_session");
				PlayerCache.getInstance().removePlayer(name);
				database.setUnlogged(name);
				plugin.management.performLogin(player, "dontneed", true);
			} else if (Settings.sessionExpireOnIpChange) {
				PlayerCache.getInstance().removePlayer(name);
				database.setUnlogged(name);
				m.send(player, "invalid_session");
			}
			return;
		}
		final BukkitTask msgT = sched.runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, msgInterval));
		LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT);
	}

	private boolean needFirstspawn() {
		if (player.hasPlayedBefore()) {
			return false;
		}
		if (Spawn.getInstance().getFirstSpawn() == null || Spawn.getInstance().getFirstSpawn().getWorld() == null) {
			return false;
		}
		final FirstSpawnTeleportEvent tpEvent = new FirstSpawnTeleportEvent(player, player.getLocation(), Spawn.getInstance().getFirstSpawn());
		plugin.getServer().getPluginManager().callEvent(tpEvent);
		if (!tpEvent.isCancelled()) {
			if (player.isOnline() && tpEvent.getTo() != null && tpEvent.getTo().getWorld() != null) {
				final Location fLoc = tpEvent.getTo();
				Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {

					@Override
					public void run() {
						player.teleport(fLoc);
					}

				});
			}
		}
		return true;

	}

	private void placePlayerSafely(final Player player, final Location spawnLoc) {
		Location loc = null;
		if (spawnLoc == null) {
			return;
		}
		if (!Settings.noTeleport) {
			return;
		}
		if (Settings.isTeleportToSpawnEnabled || (Settings.isForceSpawnLocOnJoinEnabled && Settings.getForcedWorlds.contains(player.getWorld().getName()))) {
			return;
		}
		if (!player.hasPlayedBefore()) {
			return;
		}
		final Block b = player.getLocation().getBlock();
		if (b.getType() == Material.PORTAL || b.getType() == Material.ENDER_PORTAL) {
			m.send(player, "unsafe_spawn");
			if (spawnLoc.getWorld() != null) {
				loc = spawnLoc;
			}
		} else {
			final Block c = player.getLocation().add(0D, 1D, 0D).getBlock();
			if (c.getType() == Material.PORTAL || c.getType() == Material.ENDER_PORTAL) {
				m.send(player, "unsafe_spawn");
				if (spawnLoc.getWorld() != null) {
					loc = spawnLoc;
				}
			}
		}
		if (loc != null) {
			final Location floc = loc;
			Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {

				@Override
				public void run() {
					player.teleport(floc);
				}

			});
		}
	}

}
