package poppy;

import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.WorldType;
import org.bukkit.World.Environment;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.inventory.FurnaceRecipe;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;

import poppy.modules.AutoBreakerModule;
import poppy.modules.AutoPlacerModule;
import poppy.modules.AutoWorkbenchModule;
import poppy.modules.BlockDetectorModule;
import poppy.modules.CauldronModule;
import poppy.modules.ChairModule;
import poppy.modules.DoubleDoorModule;
import poppy.modules.HopperSorterModule;
import poppy.modules.CommonModule;
import poppy.modules.LeavesDecayModule;
import poppy.modules.SpawnerModule;
import poppy.modules.PlayerMountModule;


public class Main extends JavaPlugin implements Listener
{
	private Plugin plugin;
	private Config config;
	private PluginManager pluginManager;
	
	private CommonModule commonModule;
	private DoubleDoorModule doubleDoorModule;
	private ChairModule chairModule;
	private AutoWorkbenchModule autoWorkbenchModule;
	private LeavesDecayModule leavesDecayModule;
	private CauldronModule cauldronModule;
	private SpawnerModule spawnerModule;
	private HopperSorterModule hopperSorterModule;
	private AutoPlacerModule autoPlacerModule;
	private AutoBreakerModule autoBreakerModule;
	private PlayerMountModule playerMountModule;
	private BlockDetectorModule blockDetectorModule;

	@Override
	public void onEnable()
	{
		plugin = this;
		config = new Config("config/poppy.yml");
		
		pluginManager = Bukkit.getPluginManager();

		pluginManager.registerEvents(this, this);

		commonModule = new CommonModule();
		pluginManager.registerEvents(commonModule, this);

		doubleDoorModule = new DoubleDoorModule();
		pluginManager.registerEvents(doubleDoorModule, this);

		chairModule = new ChairModule();
		pluginManager.registerEvents(chairModule, this);
		
		autoWorkbenchModule = new AutoWorkbenchModule();
		pluginManager.registerEvents(autoWorkbenchModule, this);

		leavesDecayModule = new LeavesDecayModule(this);
		pluginManager.registerEvents(leavesDecayModule, this);

		cauldronModule = new CauldronModule(this);
		pluginManager.registerEvents(cauldronModule, this);

		spawnerModule = new SpawnerModule();
		pluginManager.registerEvents(spawnerModule, this);

		hopperSorterModule = new HopperSorterModule();
		pluginManager.registerEvents(hopperSorterModule, this);

		autoPlacerModule = new AutoPlacerModule(this);
		pluginManager.registerEvents(autoPlacerModule, this);

		autoBreakerModule = new AutoBreakerModule(this);
		pluginManager.registerEvents(autoBreakerModule, this);

		playerMountModule = new PlayerMountModule();
		pluginManager.registerEvents(playerMountModule, this);

		blockDetectorModule = new BlockDetectorModule();
		pluginManager.registerEvents(blockDetectorModule, this);

		NamespacedKey key = new NamespacedKey(this, "leather");
		FurnaceRecipe recipe = new FurnaceRecipe(key, new ItemStack(Material.LEATHER), Material.ROTTEN_FLESH, 1, 200);
		Bukkit.addRecipe(recipe);

		new WorldCreator(Constants.Worlds.BACKROOMS)
			.type(WorldType.FLAT)
            .generateStructures(false)
            .createWorld();

		new WorldCreator(Constants.Worlds.SURVIVAL)
			.type(WorldType.NORMAL)
            .generateStructures(true)
            .createWorld();

		new WorldCreator(Constants.Worlds.SURVIVAL_NETHER)
			.environment(Environment.NETHER)
            .generateStructures(true)
            .createWorld();

		new WorldCreator(Constants.Worlds.SURVIVAL_THE_END)
			.environment(Environment.THE_END)
            .generateStructures(true)
            .createWorld();
	}

	@Override
	public void onDisable()
	{
		chairModule.onDisable();
	}

	@Override
	public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
	{
		if(sender instanceof Player)
		{
			Player player = (Player) sender;

			switch(label)
			{
				case "head":
					return setItemAsHead(player, args);
				case "skull":
					return getSkullForName(player, args);
				case "invsee":
					return openInventoryForPlayer(player, args);
				case "info":
					return openInfoBook(player, args);
				case "spawn":
					return teleportToSpawn(player, args);
				case "home":
					return teleportToHome(player, args);
				case "warp":
					return teleportToWarppoint(player, args);
				case "sethome":
					return setHome(player, args);
				case "setwarp":
					return setWarppoint(player, args);
				case "slime":
					return isSlimeChunk(player, args);
				case "r":
					return doReload(player, args);
				case "up":
					return placeBlockBelow(player, args);
				case "e":
					return openEnderChest(player, args);
				case "enderchest":
					return openEnderChest(player, args);
				case "world":
					return teleportToWorld(player, args);
				case "gm":
					return changeGameMode(player);
				case "heal":
					return heal(player, args);
				default:
					return false;
			}
		}

		return true;
	}

	@EventHandler
	public void onPlayerPortal(final PlayerPortalEvent event)
	{
		final Player player = event.getPlayer();
		final String worldName = player.getWorld().getName();

		if (event.getCause() == TeleportCause.NETHER_PORTAL)
		{
			if (worldName.equals(Constants.Worlds.SURVIVAL))
			{
				final Location location = new Location(
					Bukkit.getWorld(Constants.Worlds.SURVIVAL_NETHER),
					event.getFrom().getBlockX() / 8,
					event.getFrom().getBlockY(),
					event.getFrom().getBlockZ() / 8
				);
				event.setTo(location);
			}
			else if (worldName.equals(Constants.Worlds.SURVIVAL_NETHER))
			{
				final Location location = new Location(
					Bukkit.getWorld(Constants.Worlds.SURVIVAL),
					event.getFrom().getBlockX() * 8,
					event.getFrom().getBlockY(),
					event.getFrom().getBlockZ() * 8
				);
				event.setTo(location);
			}
		}

		if (event.getCause() == TeleportCause.END_PORTAL)
		{
			if (worldName.equals(Constants.Worlds.SURVIVAL))
			{
        final Location location = new Location(
					Bukkit.getWorld(Constants.Worlds.SURVIVAL_THE_END),
					100,
					50,
					0
				);
        event.setTo(location);

        final Block block = location.getBlock();

        for (int x = block.getX() - 2; x <= block.getX() + 2; x++)
				{
          for (int z = block.getZ() - 2; z <= block.getZ() + 2; z++)
					{
            final Block platformBlock = location.getWorld().getBlockAt(x, block.getY() - 1, z);

            if (platformBlock.getType() != Material.OBSIDIAN)
						{
              platformBlock.setType(Material.OBSIDIAN);
            }

            for (int yMod = 1; yMod <= 3; yMod++)
						{
              final Block b = platformBlock.getRelative(BlockFace.UP, yMod);

              if (b.getType() != Material.AIR)
							{
                b.setType(Material.AIR);
              }
            }
          }
        }
      }
			else if (worldName.equals(Constants.Worlds.SURVIVAL_THE_END))
			{
        event.setTo(Bukkit.getWorld(Constants.Worlds.SURVIVAL).getSpawnLocation());
      }
		}
	}

	private boolean setItemAsHead(final Player player, final String[] args)
	{
		final ItemStack item = player.getInventory().getItemInMainHand();
		final ItemStack head = player.getInventory().getHelmet();

		if(head != null && head.getType().equals(Material.SADDLE))
		{
			player.eject();
		}

		player.getInventory().setHelmet(item);
		player.getInventory().setItemInMainHand((new ItemStack(Material.AIR)));

		if(player.getGameMode() == GameMode.SURVIVAL)
		{
			if(head != null)
			{
				player.getInventory().addItem(head);
			}
		}

		return true;
	}
	
	@SuppressWarnings("deprecation")
	private boolean getSkullForName(final Player player, final String[] args)
	{
		if(!player.isOp())
		{
			player.sendMessage(Constants.Messages.NOT_ENOUGH_PERMISSION);
			return true;
		}

		if(args.length != 1)
		{
			return false;
		}

		final String name = args[0];
		final ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
		final SkullMeta meta = (SkullMeta) skull.getItemMeta();
		meta.setOwner(name);
		skull.setItemMeta(meta);

		player.getInventory().addItem(skull);

		return true;
	}

	private boolean openInventoryForPlayer(final Player player, final String[] args)
	{
		if(args.length == 0)
		{
			return false;
		}

		if(!player.isOp()) 
		{
			player.sendMessage(Constants.Messages.NOT_ENOUGH_PERMISSION);
			return true;
		}

		final Player target = Bukkit.getPlayer(args[0]);

		if(target == null)
		{
			player.sendMessage(ChatColor.RED + "Player not found!");
			return true;
		}

		player.openInventory(target.getInventory());
		
		return true;
	}

	private boolean openInfoBook(final Player player, final String[] args)
	{
		final ItemStack book = new ItemStack(Material.WRITTEN_BOOK);
		final BookMeta meta = (BookMeta) book.getItemMeta();
		meta.setTitle("Info");
		meta.setAuthor("Server");
		meta.setPages(config.getInfoPages());
		book.setItemMeta(meta);

		player.openBook(book);

		return true;
	}
	
	private boolean teleportToSpawn(final Player player, final String[] args)
	{
		final Location location = Bukkit.getWorld("world").getSpawnLocation();

		if(Utils.travelTo(plugin, player, location))
		{
			player.sendMessage(ChatColor.GRAY + "You've been teleported to spawn.");
		}

		return true;
	}

	private boolean teleportToHome(final Player player, final String[] args)
	{
		final Location location = config.getHome(player);

		if(location == null)
		{
			player.sendMessage(ChatColor.RED + "You've don't have a home point.");
			return true;
		}
		
		if(Utils.travelTo(plugin, player, location))
		{
			player.sendMessage(ChatColor.GRAY + "You've been teleported to your home.");
		}

		return true;
	}

	private boolean teleportToWarppoint(final Player player, final String[] args)
	{
		if(args.length != 1)
		{
			return false;
		}
		
		final String name = args[0].toLowerCase();
		final Location location = config.getWarp(name);

		if(location == null)
		{
			player.sendMessage(ChatColor.RED + "Warppoint " + name + " doesn't exist!");
			return true;
		}
		
		if(Utils.travelTo(plugin, player, location))
		{
			player.sendMessage(ChatColor.GRAY + "You've been teleported to warppoint " + name + ".");
		}

		return true;
	}

	private boolean setHome(final Player player, final String[] args)
	{
		try
		{
			config.setHome(player);
			player.sendMessage(ChatColor.YELLOW + "Home point set.");
		}
		catch(Exception ex)
		{
			player.sendMessage(Constants.Messages.FAILED_TO_SAVE_CONFIG_FILE);
		}

		return true;
	}

	private boolean setWarppoint(final Player player, final String[] args)
	{
		if(args.length != 1)
		{
			return false;
		}
		
		final String name = args[0].toLowerCase();

		if(!Utils.payFee(player, Material.COMPASS, 1))
		{
			player.sendMessage(ChatColor.RED + "The fee for creating a warppoint is 1 compass!");
			return true;
		}

		try
		{
			if(!config.setWarp(name, player.getLocation()))
			{
				player.sendMessage(ChatColor.RED + "Warppoint " + name + " already exists!");
				return true;
			}
			
			player.sendMessage(ChatColor.YELLOW + "Warppoint " + name + " set.");
		}
		catch(Exception ex)
		{
			player.sendMessage(Constants.Messages.FAILED_TO_SAVE_CONFIG_FILE);
		}

		return true;
	}

	private boolean isSlimeChunk(final Player player, final String[] args)
	{
		boolean isSlimeChunk = player.getLocation().getChunk().isSlimeChunk();

		if(isSlimeChunk)
		{
			player.sendMessage(ChatColor.YELLOW + "This is a slime chunk!");
			return true;
		}
		else
		{
			player.sendMessage(ChatColor.RED  + "This is not a slime chunk!");
			return true;
		}
	}

	private boolean doReload(final Player player, final String[] args)
	{
		player.chat("/reload confirm");
		return true;
	}

	private boolean placeBlockBelow(final Player player, final String[] args)
	{
		Location playerLocation = player.getLocation().clone();
		Block blockBelow = playerLocation.add(0, -1, 0).getBlock();

		if(!Utils.isAir(blockBelow.getType()))
		{
			return false;
		}

		if(!player.isOp()) 
		{
			player.sendMessage(Constants.Messages.NOT_ENOUGH_PERMISSION);
			return true;
		}

		blockBelow.setType(Material.GLASS);

		return true;
	}

	private boolean openEnderChest(final Player player, final String[] args)
	{
		if(args.length != 0)
		{
			if(!player.isOp()) 
			{
				player.sendMessage(Constants.Messages.NOT_ENOUGH_PERMISSION);
				return true;
			}

			final Player target = Bukkit.getPlayer(args[0]);

			if(target == null)
			{
				player.sendMessage(ChatColor.RED + "Player not found!");
				return true;
			}

			player.openInventory(target.getEnderChest());

		}
		else
		{
			player.openInventory(player.getEnderChest());
		}

		return true;
	}

	private boolean teleportToWorld(final Player player, final String[] args)
	{
		if (args.length != 1)
		{
			return false;
		}
		
		final String name = args[0].toLowerCase();
		final World world = Bukkit.getServer().getWorld(name);

		if (world == null)
		{
			player.sendMessage(ChatColor.RED + "World " + name + " doesn't exist!");
			return true;
		}

		player.teleport(world.getSpawnLocation());

		if (world.getName().equals(Constants.Worlds.BACKROOMS))
		{
			player.setGameMode(GameMode.ADVENTURE);
		}
		else
		{
			player.setGameMode(GameMode.SURVIVAL);
		}
		
		player.sendMessage(ChatColor.GRAY + "You've been teleported to world " + name + ".");

		return true;
	}

	private boolean changeGameMode(final Player player)
	{
		if (!Constants.Worlds.DEFAULT.equals(player.getWorld().getName())) {
			player.sendMessage(ChatColor.RED + "This is not a creative world!");
			return true;
		}

		if (GameMode.SURVIVAL.equals(player.getGameMode()))
		{
			player.setGameMode(GameMode.CREATIVE);
			player.sendMessage(ChatColor.GRAY + "Changed gamemode to creative.");
		}
		else
		{
			player.setGameMode(GameMode.SURVIVAL);
			player.sendMessage(ChatColor.GRAY + "Changed gamemode to survival.");
		}

		return true;
	}

	private boolean heal(final Player player, final String[] args)
	{
		if(args.length != 0)
		{
			Player argPlayer = Bukkit.getPlayer(args[0]);

			if(argPlayer == null)
			{
				player.sendMessage("Player" + args[0] + " not found");
				return false;

			}

			argPlayer.setHealth(20);
			argPlayer.setFoodLevel(20);

			player.sendMessage("You have healed  " + argPlayer.getName());
			argPlayer.sendMessage("You have been healed");
		}
		else
		{
			player.setHealth(20);
			player.setFoodLevel(20);

			player.sendMessage("You have been healed");
		}
		
		return true;
	}
}
