package me.wiefferink.areashop.managers;

import me.wiefferink.areashop.features.signs.RegionSign;
import me.wiefferink.areashop.features.signs.SignsFeature;
import me.wiefferink.areashop.regions.GeneralRegion;
import me.wiefferink.areashop.tools.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.material.Sign;
import org.bukkit.util.BlockIterator;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

public class SignLinkerManager extends Manager implements Listener {
	private final Map<UUID, SignLinker> signLinkers;
	private boolean eventsRegistered;

	public SignLinkerManager() {
		signLinkers = new HashMap<>();
		eventsRegistered = false;
	}

	@Override
	public void shutdown() {
		for(UUID uuid : signLinkers.keySet()) {
			exitSignLinkMode(Bukkit.getPlayer(uuid));
		}
	}

	/**
	 * Let a player enter sign linking mode.
	 * @param player  The player that has to enter sign linking mode
	 * @param profile The profile to use for the signs (null for default)
	 */
	public void enterSignLinkMode(Player player, String profile) {
		signLinkers.put(player.getUniqueId(), new SignLinker(player, profile));
		plugin.message(player, "linksigns-first");
		plugin.message(player, "linksigns-next");
		if(!eventsRegistered) {
			eventsRegistered = true;
			plugin.getServer().getPluginManager().registerEvents(this, plugin);
		}
	}

	/**
	 * Let a player exit sign linking mode.
	 * @param player The player that has to exit sign linking mode
	 */
	public void exitSignLinkMode(Player player) {
		signLinkers.remove(player.getUniqueId());
		if(eventsRegistered && signLinkers.isEmpty()) {
			eventsRegistered = false;
			HandlerList.unregisterAll(this);
		}
		plugin.message(player, "linksigns-stopped");
	}

	/**
	 * Check if the player is in sign linking mode.
	 * @param player The player to check
	 * @return true if the player is in sign linking mode, otherwise false
	 */
	public boolean isInSignLinkMode(Player player) {
		return signLinkers.containsKey(player.getUniqueId());
	}

	/**
	 * On player interactions.
	 * @param event The PlayerInteractEvent
	 */
	@EventHandler(priority = EventPriority.HIGH)
	public void onPlayerInteract(PlayerInteractEvent event) {
		if(isInSignLinkMode(event.getPlayer())) {
			event.setCancelled(true);
			Player player = event.getPlayer();
			SignLinker linker = signLinkers.get(event.getPlayer().getUniqueId());
			if(event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
				// Get the region
				BlockIterator blockIterator = new BlockIterator(player, 100);
				while(blockIterator.hasNext()) {
					Block next = blockIterator.next();
					List<GeneralRegion> regions = Utils.getRegions(next.getLocation());
					if(regions.size() == 1) {
						linker.setRegion(regions.get(0));
						return;
					} else if(regions.size() > 1) {
						Set<String> names = new HashSet<>();
						for(GeneralRegion region : regions) {
							names.add(region.getName());
						}
						plugin.message(player, "linksigns-multipleRegions", Utils.createCommaSeparatedList(names));
						plugin.message(player, "linksigns-multipleRegionsAdvice");
						return;
					}
				}
				// No regions found within the maximum range
				plugin.message(player, "linksigns-noRegions");
			} else if(event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) {
				Block block = null;
				BlockIterator blockIterator = new BlockIterator(player, 100);
				while(blockIterator.hasNext() && block == null) {
					Block next = blockIterator.next();
					if(next.getType() != Material.AIR) {
						block = next;
					}
				}
				if(block == null || !(block.getType() == Material.WALL_SIGN || block.getType() == Material.SIGN_POST)) {
					plugin.message(player, "linksigns-noSign");
					return;
				}

				RegionSign regionSign = SignsFeature.getSignByLocation(block.getLocation());
				if(regionSign != null) {
					plugin.message(player, "linksigns-alreadyRegistered", regionSign.getRegion());
					return;
				}
				Sign sign = (Sign)block.getState().getData();
				linker.setSign(block.getLocation(), block.getType(), sign.getFacing());
			}
		}
	}

	/**
	 * Handle disconnection players.
	 * @param event The PlayerQuitEvent
	 */
	@EventHandler(priority = EventPriority.MONITOR)
	public void onPlayerLeave(PlayerQuitEvent event) {
		exitSignLinkMode(event.getPlayer());
	}

	/**
	 * Class to keep track of the signlinking data.
	 */
	private class SignLinker {
		private boolean hasSign = false;
		private boolean hasRegion = false;

		public final Player linker;
		public final String profile;

		public GeneralRegion region = null;

		public Location location = null;
		public Material type = null;
		public BlockFace facing = null;

		public SignLinker(Player linker, String profile) {
			this.linker = linker;
			this.profile = profile;
		}

		public void setRegion(GeneralRegion region) {
			this.region = region;
			hasRegion = true;
			if(!isComplete()) {
				plugin.message(linker, "linksigns-regionFound", region);
			}
			finish();
		}

		public void setSign(Location location, Material type, BlockFace facing) {
			this.location = location;
			this.type = type;
			this.facing = facing;
			hasSign = true;
			if(!isComplete()) {
				plugin.message(linker, "linksigns-signFound", location.getBlockX(), location.getBlockY(), location.getBlockZ());
			}
			finish();
		}

		public void finish() {
			if(isComplete()) {
				region.getSignsFeature().addSign(location, type, facing, profile);
				if(profile == null) {
					plugin.message(linker, "addsign-success", region);
				} else {
					plugin.message(linker, "addsign-successProfile", region, profile);
				}
				region.update();
				reset();

				plugin.message(linker, "linksigns-next");
			}
		}

		public void reset() {
			hasSign = false;
			hasRegion = false;
		}

		public boolean isComplete() {
			return hasSign && hasRegion;
		}
	}


}
















