package de.flo56958.minetinker.modifiers.types;

import de.flo56958.minetinker.MineTinker;
import de.flo56958.minetinker.data.ToolType;
import de.flo56958.minetinker.modifiers.Modifier;
import de.flo56958.minetinker.utils.ChatWriter;
import de.flo56958.minetinker.utils.ConfigurationManager;
import de.flo56958.minetinker.utils.LanguageManager;
import de.flo56958.minetinker.utils.PlayerInfo;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Creature;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityTargetLivingEntityEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask;

import java.io.File;
import java.util.*;

public class ShadowDive extends Modifier implements Listener {

	private static ShadowDive instance;

	private int requiredLightLevel;
	private final HashSet<Player> activePlayers = new HashSet<>();

	private BukkitTask task;
	private ShadowDive() {
		super(MineTinker.getPlugin());
		customModelData = 10_052;
	}

	public static ShadowDive instance() {
		synchronized (ShadowDive.class) {
			if (instance == null) {
				instance = new ShadowDive();
			}
		}

		return instance;
	}

	private final Runnable runnable = new Runnable() {
		@Override
		public void run() {
			Iterator<Player> iterator = activePlayers.iterator();
			//noinspection WhileLoopReplaceableByForEach
			while (iterator.hasNext()) {
				Player p = iterator.next();
				Location loc = p.getLocation();
				byte lightlevel = p.getWorld().getBlockAt(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()).getLightLevel();
				if (!p.isSneaking() || lightlevel > requiredLightLevel || p.hasPotionEffect(PotionEffectType.GLOWING)) {
					showPlayer(p);
					ChatWriter.sendActionBar(p, ChatColor.RED + ShadowDive.instance().getName() + ": "
							+ LanguageManager.getString("Modifier.Shadow-Dive.LightToHigh", p));
				} else if (PlayerInfo.isCombatTagged(p)) {
					showPlayer(p);
					ChatWriter.sendActionBar(p, ChatColor.RED + ShadowDive.instance().getName() + ": "
							+ LanguageManager.getString("Modifier.Shadow-Dive.InCombat", p));
				} else {
					for(Player pl : Bukkit.getOnlinePlayers()) {
						if (pl.equals(p)) continue;
						if (pl.hasPotionEffect(PotionEffectType.NIGHT_VISION)) {
							pl.showPlayer(MineTinker.getPlugin(), p);
						} else {
							pl.hidePlayer(MineTinker.getPlugin(), p);
						}
					}
				}
			}
		}
	};

	@Override
	public String getKey() {
		return "Shadow-Dive";
	}

	@Override
	public List<ToolType> getAllowedTools() {
		return Collections.singletonList(ToolType.BOOTS);
	}

	@Override
	public void reload() {
		if (task != null) {
			task.cancel();
			task = null;
		}
		FileConfiguration config = getConfig();
		config.options().copyDefaults(true);

		config.addDefault("Allowed", true);
		config.addDefault("Color", "%GRAY%");
		config.addDefault("MaxLevel", 1);
		config.addDefault("SlotCost", 3);
		config.addDefault("RequiredLightLevel", 6);

		config.addDefault("EnchantCost", 10);
		config.addDefault("Enchantable", false);

		config.addDefault("Recipe.Enabled", true);
		config.addDefault("Recipe.Top", "CDC");
		config.addDefault("Recipe.Middle", "DDD");
		config.addDefault("Recipe.Bottom", "CDC");

		Map<String, String> recipeMaterials = new HashMap<>();
		recipeMaterials.put("D", Material.DIAMOND.name());
		recipeMaterials.put("C", Material.COBWEB.name());

		config.addDefault("Recipe.Materials", recipeMaterials);

		ConfigurationManager.saveConfig(config);
		ConfigurationManager.loadConfig("Modifiers" + File.separator, getFileName());

		init(Material.DIAMOND);
		this.requiredLightLevel = config.getInt("RequiredLightLevel", 6);

		this.description = this.description.replaceAll("%level", String.valueOf(this.requiredLightLevel));

		if (this.isAllowed()) task = Bukkit.getScheduler().runTaskTimer(MineTinker.getPlugin(), runnable, 0,5);
	}

	private void hidePlayer(Player p) {
		activePlayers.add(p);

		//Clear all mob targets
		Collection<Entity> nearbyEntities = p.getWorld().getNearbyEntities(p.getLocation(), 64, 64, 64);
		for (Entity ent : nearbyEntities) {
			if (ent instanceof Creature) {
				if (p.equals(((Creature) ent).getTarget())) {
					((Creature) ent).setTarget(null);
				}
			}
		}

		//Hide from all players
		for (Player player : Bukkit.getServer().getOnlinePlayers()) {
			if (!p.equals(player)) {
				if (!player.hasPotionEffect(PotionEffectType.NIGHT_VISION)) player.hidePlayer(MineTinker.getPlugin(), p);
			}
		}
	}

	private void showPlayer(Player p) {
		if (activePlayers.remove(p)) {
			for (Player player : Bukkit.getServer().getOnlinePlayers()) {
				if (!p.equals(player)) player.showPlayer(MineTinker.getPlugin(), p);
			}
		}
	}

	@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
	public void onJoin(PlayerJoinEvent event) {
		for(Player p : activePlayers) {
			event.getPlayer().hidePlayer(MineTinker.getPlugin(), p);
		}
	}

	@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
	public void onLeave(PlayerQuitEvent event) {
		for(Player p : activePlayers) {
			event.getPlayer().showPlayer(MineTinker.getPlugin(), p);
		}
	}

	@EventHandler(ignoreCancelled = true)
	public void onTarget(EntityTargetLivingEntityEvent event) {
		if (event.getTarget() instanceof Player) {
			//noinspection SuspiciousMethodCalls
			if (activePlayers.contains(event.getTarget())) {
				event.setCancelled(true);
			}
		}
	}

	@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
	public void onSneak(PlayerToggleSneakEvent event) {
		Player player = event.getPlayer();
		if (!player.hasPermission("minetinker.modifiers.shadowdive.use")) return;

		ItemStack boots = player.getInventory().getBoots();
		if (!modManager.isArmorViable(boots)) return;
		if (!modManager.hasMod(boots, this)) return;

		if (event.isSneaking() && !player.isGliding()) { //enable
			Location loc = player.getLocation();
			byte lightlevel = player.getWorld().getBlockAt(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()).getLightLevel();
			boolean combatTagged = PlayerInfo.isCombatTagged(player);
			ChatWriter.logModifier(player, event, this, boots,
					String.format("LightLevel(%d/%d)", lightlevel, this.requiredLightLevel),
					String.format("InCombat(%b)", combatTagged));
			if (lightlevel > this.requiredLightLevel || player.hasPotionEffect(PotionEffectType.GLOWING)) {
				ChatWriter.sendActionBar(player, ChatColor.RED + this.getName() + ": "
						+ LanguageManager.getString("Modifier.Shadow-Dive.LightToHigh", player));
				return;
			}

			if (combatTagged) {
				ChatWriter.sendActionBar(player, ChatColor.RED + this.getName() + ": "
						+ LanguageManager.getString("Modifier.Shadow-Dive.InCombat", player));
				return;
			}

			hidePlayer(player);

		} else { //disable
			if (!activePlayers.contains(player)) return;
			showPlayer(player);
		}
	}
}