package cn.citycraft.AuthMe;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLConnection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.zip.GZIPInputStream;

import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;

import com.maxmind.geoip.LookupService;

import cn.citycraft.AuthMe.cache.auth.PlayerCache;
import cn.citycraft.AuthMe.cache.limbo.LimboCache;
import cn.citycraft.AuthMe.cache.limbo.LimboPlayer;
import cn.citycraft.AuthMe.settings.Settings;
import fr.xephi.authme.events.AuthMeTeleportEvent;

public class Utils {

	public static AuthMe plugin;

	private static Method getOnlinePlayers;
	private static boolean getOnlinePlayersIsCollection;
	private static LookupService lookupService;

	static {
		plugin = AuthMe.getInstance();
		checkGeoIP();
		try {
			final Method m = Bukkit.class.getDeclaredMethod("getOnlinePlayers");
			getOnlinePlayersIsCollection = m.getReturnType() == Collection.class;
		} catch (final Exception ignored) {
		}
	}

	public static boolean addNormal(final Player player, final String group) {
		if (!useGroupSystem()) {
			return false;
		}
		if (plugin.permission == null) {
			return false;
		}
		try {
			if (plugin.permission.playerRemoveGroup(player, Settings.getUnloggedinGroup) && plugin.permission.playerAddGroup(player, group)) {
				return true;
			}
		} catch (final UnsupportedOperationException e) {
			ConsoleLogger.showError("Your permission system (" + plugin.permission.getName() + ") do not support Group system with that config... unhook!");
			plugin.permission = null;
			return false;
		}
		return false;
	}

	// TODO: Move to a Manager
	public static boolean checkAuth(final Player player) {
		if (player == null || Utils.isUnrestricted(player)) {
			return true;
		}

		final String name = player.getName().toLowerCase();
		if (PlayerCache.getInstance().isAuthenticated(name)) {
			return true;
		}

		if (!Settings.isForcedRegistrationEnabled) {
			if (!plugin.database.isAuthAvailable(name)) {
				return true;
			}
		}

		return false;
	}

	// Check and Download GeoIP data if not exist
	public static boolean checkGeoIP() {
		if (lookupService != null) {
			return true;
		}
		ConsoleLogger.info("[LICENSE] IP数据来自 CityCraft 的 Yum 源 原始数据来自 http://www.maxmind.com");
		final File file = new File(Settings.APLUGIN_FOLDER, "GeoIP.dat");
		try {
			if (file.exists()) {
				if (lookupService == null) {
					lookupService = new LookupService(file);
					return true;
				}
			}
			ConsoleLogger.info("从 CityCraft 的 Yum 源 下载IP缓存库...");
			final String url = "http://citycraft.cn/downloads/authme/GeoIP.dat.gz";
			final URL downloadUrl = new URL(url);
			final URLConnection conn = downloadUrl.openConnection();
			conn.setConnectTimeout(10000);
			conn.connect();
			InputStream input = conn.getInputStream();
			if (conn.getURL().toString().endsWith(".gz")) {
				input = new GZIPInputStream(input);
			}
			final OutputStream output = new FileOutputStream(file);
			final byte[] buffer = new byte[2048];
			int length = input.read(buffer);
			while (length >= 0) {
				output.write(buffer, 0, length);
				length = input.read(buffer);
			}
			output.close();
			input.close();
		} catch (final Exception e) {
			ConsoleLogger.writeStackTrace(e);
			return false;
		}
		return checkGeoIP();
	}

	/*
	 * Used for force player GameMode
	 */
	public static void forceGM(final Player player) {
		if (!plugin.authmePermissible(player, "authme.bypassforcesurvival")) {
			player.setGameMode(GameMode.SURVIVAL);
		}
	}

	public static String getCountryCode(final String ip) {
		if (checkGeoIP()) {
			return lookupService.getCountry(ip).getCode();
		}
		return "--";
	}

	public static String getCountryName(final String ip) {
		if (checkGeoIP()) {
			return lookupService.getCountry(ip).getName();
		}
		return "N/A";
	}

	@SuppressWarnings("unchecked")
	public static Collection<? extends Player> getOnlinePlayers() {
		if (getOnlinePlayersIsCollection) {
			return Bukkit.getOnlinePlayers();
		}
		try {
			if (getOnlinePlayers == null) {
				getOnlinePlayers = Bukkit.class.getMethod("getOnlinePlayers");
			}
			final Object obj = getOnlinePlayers.invoke(null);
			if (obj instanceof Collection) {
				return (Collection<? extends Player>) obj;
			}
			return Arrays.asList((Player[]) obj);
		} catch (final Exception ignored) {
		}
		return Collections.emptyList();
	}

	// TODO: remove if not needed
	public static void hasPermOnJoin(final Player player) {
		if (plugin.permission == null) {
			return;
		}
		for (final String permission : Settings.getJoinPermissions) {
			if (plugin.permission.playerHas(player, permission)) {
				plugin.permission.playerAddTransient(player, permission);
			}
		}
	}

	public static boolean isNPC(final Entity player) {
		return player.hasMetadata("NPC");
	}

	public static boolean isUnrestricted(final Player player) {
		return Settings.isAllowRestrictedIp && !Settings.getUnrestrictedName.isEmpty() && (Settings.getUnrestrictedName.contains(player.getName()));
	}

	public static void packCoords(final double x, final double y, final double z, final String w, final Player pl) {
		World theWorld;
		if (w.equals("unavailableworld")) {
			theWorld = pl.getWorld();
		} else {
			theWorld = Bukkit.getWorld(w);
		}
		if (theWorld == null) {
			theWorld = pl.getWorld();
		}
		final World world = theWorld;
		final Location locat = new Location(world, x, y, z);

		Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {

			@Override
			public void run() {
				final AuthMeTeleportEvent tpEvent = new AuthMeTeleportEvent(pl, locat);
				plugin.getServer().getPluginManager().callEvent(tpEvent);
				Utils.safeTP(pl, locat);
			}
		});
	}

	public static void purgeDirectory(final File file) {
		if (!file.isDirectory()) {
			return;
		}
		final File[] files = file.listFiles();
		if (files == null) {
			return;
		}
		for (final File target : files) {
			if (target.isDirectory()) {
				purgeDirectory(target);
				target.delete();
			} else {
				target.delete();
			}
		}
	}

	public static void safeTP(final Player p, final Location loc) {
		if (!loc.getWorld().getChunkAt(loc).isLoaded()) {
			loc.getWorld().getChunkAt(loc).load();
		}
		p.teleport(loc);
	}

	public static void setGroup(final Player player, final GroupType group) {
		if (!Settings.isPermissionCheckEnabled) {
			return;
		}
		if (plugin.permission == null) {
			return;
		}
		String currentGroup;
		try {
			currentGroup = plugin.permission.getPrimaryGroup(player);
		} catch (final UnsupportedOperationException e) {
			ConsoleLogger.showError("Your permission plugin (" + plugin.permission.getName() + ") doesn't support the Group system... unhook!");
			plugin.permission = null;
			return;
		}
		switch (group) {
		case UNREGISTERED: {
			plugin.permission.playerRemoveGroup(player, currentGroup);
			plugin.permission.playerAddGroup(player, Settings.unRegisteredGroup);
			break;
		}
		case REGISTERED: {
			plugin.permission.playerRemoveGroup(player, currentGroup);
			plugin.permission.playerAddGroup(player, Settings.getRegisteredGroup);
			break;
		}
		case NOTLOGGEDIN: {
			if (!useGroupSystem()) {
				break;
			}
			plugin.permission.playerRemoveGroup(player, currentGroup);
			plugin.permission.playerAddGroup(player, Settings.getUnloggedinGroup);
			break;
		}
		case LOGGEDIN: {
			if (!useGroupSystem()) {
				break;
			}
			final LimboPlayer limbo = LimboCache.getInstance().getLimboPlayer(player.getName().toLowerCase());
			if (limbo == null) {
				break;
			}
			final String realGroup = limbo.getGroup();
			plugin.permission.playerRemoveGroup(player, currentGroup);
			plugin.permission.playerAddGroup(player, realGroup);
			break;
		}
		}
	}

	private static boolean useGroupSystem() {
		return Settings.isPermissionCheckEnabled && !Settings.getUnloggedinGroup.isEmpty();
	}

	public enum GroupType {
		LOGGEDIN,
		NOTLOGGEDIN,
		REGISTERED,
		UNREGISTERED
	}
}
