public class ChatHandler {

    private TweakcraftUtils plugin;
    private AntiSpam antispam = null;
    private Map<String, ChatMode> chatmodes = new HashMap<String, ChatMode>();
    private Map<String, String> playerchatmode = new HashMap<String, String>();
    private Map<String, Long> mutedPlayers = new HashMap<String, Long>();

    public ChatHandler(TweakcraftUtils instance) {
        plugin = instance;
        chatmodes.clear();
        this.registerChatMode("admin", new AdminChat(this)); /* This one has a higher priority! */
        this.registerChatMode("local", new LocalChat(this));
        this.registerChatMode("region", new RegionChat(this));
        this.registerChatMode("zones", new ZoneChat(this));
        this.registerChatMode("world", new WorldChat(this));
    }
    
    public boolean registerChatMode(String chatModeLabel, ChatMode chatMode) {
        if(chatmodes.containsValue(chatMode)) return false;
        
        chatmodes.put(chatModeLabel, chatMode);
        return true;
    }

    public void enableAntiSpam() {
        if (plugin.getConfigHandler().enableSpamControl && antispam == null)
            this.antispam = new AntiSpam(this, plugin.getConfigHandler());
    }

    public ChatMode getChatMode(String mode) throws ChatModeException {
        if (chatmodes.containsKey(mode)) {
            return chatmodes.get(mode);
        } else {
            throw new ChatModeException("No chat mode found by that name!");
        }
    }

    public ChatMode getPlayerChatMode(Player player) {
        if (playerchatmode.containsKey(player.getName())) {
            return chatmodes.get(playerchatmode.get(player.getName()));
        }
        return null;
    }

    public TweakcraftUtils getTCUtilsInstance() {
        return plugin;
    }

    public List<String> listChatModes() {
        List<String> l = new ArrayList<String>();
        for (Map.Entry<String, ChatMode> s : chatmodes.entrySet()) {
            if(s.getValue().isEnabled() && !s.getValue().isHidden())
                l.add(s.getKey());
        }
        return l;
    }

    public String getPlayerChatModeString(String player) {
        if (playerchatmode.containsKey(player)) {
            return playerchatmode.get(player);
        }
        return null;
    }


    public String getBypassChar() {
        return "!";
    }

    public void removePlayer(Player player) {
        for (ChatMode cm : chatmodes.values()) {
            cm.removeRecipient(player.getName());
        }
    }


    public void setPlayerchatmode(String player, String mode) throws ChatModeException {
        if (playerchatmode.containsKey(player)) {
            try {
                ChatMode cm = getChatMode(playerchatmode.get(player));
                if (!(cm instanceof AdminChat)) {
                    cm.removeRecipient(player);
                }
            } catch (ChatModeException e) {

            }
        }
        if (mode == null) {
            playerchatmode.remove(player);
        } else {
            ChatMode cm = getChatMode(mode);
            if (cm instanceof AdminChat) {
                Player p = plugin.getServer().getPlayer(player);
                if (p != null)
                    if (!plugin.check(p, "admon"))
                        cm.addRecipient(player);
            }
            playerchatmode.put(player, mode);
        }
        if (plugin.getConfigHandler().enableCUI) {
            Player p = plugin.getServer().getPlayer(player);
            if (p != null)
                plugin.sendCUIChatMode(p);
        }
    }

    public void addMute(String player) {
        addMute(player, null);
    }

    public void updateMute(String player, Long toTime) {
        mutedPlayers.put(player, toTime);
    }

    public AntiSpam getAntiSpam() {
        return this.antispam;
    }

    public void addMute(String player, Long duration) {
        Long toTime = null;

        if (player != null) player = player.toLowerCase(); /* Sanity checks */
        else return;

        if (duration != null) {
            toTime = Calendar.getInstance().getTime().getTime();
            toTime += duration * 1000;
        }
        if (plugin.getConfigHandler().enablePersistence) {
            PlayerOptions po = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", player).ieq("optionname", "mute").findUnique();
            if (po == null) {
                po = new PlayerOptions();
                po.setName(player);
                po.setOptionname("mute");
            }

            po.setOptionvalue(toTime == null ? null : toTime.toString());
            plugin.getDatabase().save(po);
        }
        mutedPlayers.put(player, toTime);
    }

    public boolean canTalk(String player) {
        if (mutedPlayers.containsKey(player.toLowerCase())) {
            Long checktime = Calendar.getInstance().getTime().getTime();
            Long muteTime = mutedPlayers.get(player.toLowerCase());
            if (muteTime == null || checktime < muteTime) {
                return false;
            }
            if (checktime > muteTime) {
                if (plugin.getConfigHandler().enableDebug)
                    plugin.getLogger().info(" Mutes: auto-unmuting " + player + ", his mutetime was over!");
                removeMute(player);
            }
        }
        return true;
    }

    public void removeMute(String player) {
        if (mutedPlayers.containsKey(player)) {
            mutedPlayers.remove(player);
        }
        if (plugin.getConfigHandler().enablePersistence) {
            PlayerOptions po = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", player).ieq("optionname", "mute").findUnique();
            if (po != null)
                plugin.getDatabase().delete(po);
        }
    }

    public Map<String, Long> getMutedPlayers() {
        return mutedPlayers;
    }

    public Long getRemaining(String player) {
        if (canTalk(player)) return null;
        Long remain = mutedPlayers.get(player);
        if (remain == null) return null;
        Long checktime = Calendar.getInstance().getTime().getTime();
        Double dres = Math.floor((remain - checktime) / 1000);
        return dres.longValue();
    }

    public boolean isMuted(String playername) {
        return mutedPlayers.containsKey(playername.toLowerCase());
    }
} public abstract class ChatMode {

    protected Set<String> subscribers;
    protected ChatHandler chathandler;
    protected String chatModeName = null;
    protected Logger logger = Logger.getLogger("Minecraft");

    public ChatMode(ChatHandler instance) {
        this.subscribers = new HashSet<String>();
        this.chathandler = instance;
    }

    /**
     * Sends a message in the chosen method
     *
     * @param sender
     * @param message
     * @return
     */
    public boolean sendMessage(CommandSender sender, String message) {

        String playerMessage = String.format(getChatFormatString(), sender instanceof Player ? ((Player) sender).getDisplayName() : ChatColor.LIGHT_PURPLE + "CONSOLE" + ChatColor.WHITE, message);
        for (Player player : getRecipients(sender)) {
            player.sendMessage(playerMessage);
        }

        logChat(sender, message);

        return true;
    }

    /**
     * Broadcasts a message in the ChatMode without a playertag
     *
     * @param message
     * @return
     */
    public boolean broadcastMessage(String message) {
        // String msg = message;
        for (Player p : getRecipients(null)) {
            p.sendMessage(message);
        }

        logChat(message);
        return true;
    }

    /**
     * Returns a list of players that will get the message
     *
     * @return
     */
    public abstract Set<Player> getRecipients(CommandSender sender);

    /**
     * Tries to remove a recipient
     *
     * @param player
     */
    public void addRecipient(String player) {
        if (!subscribers.contains(player)) {
            subscribers.add(player);
        }
    }

    /**
     * Broadcasts a message in the current ChatMode
     *
     * @param sender
     * @param message
     * @return if the broadcast was successful
     */
    public abstract boolean broadcastMessage(CommandSender sender, String message);

    /**
     * Tries to remove a recipient
     *
     * @param player
     */
    public void removeRecipient(String player) {
        if (subscribers.contains(player)) {
            subscribers.remove(player);
        }
    }

    /**
     * Gets the current subscribed list
     *
     * @return
     */
    public Set<String> getSubscribers() {
        return this.subscribers;
    }

    /**
     * Shows a little description about the ChatMode
     *
     * @return The description
     */
    public abstract String getDescription();

    /**
     * Check if it's enabled
     *
     * @return enabled
     */
    public boolean isEnabled() {
        return false;
    }


    /**
     * Gets the ChatMode's color
     *
     * @return the color
     */
    public abstract String getColor();

    /**
     * Returns the ChatMode's prefix
     *
     * @return the prefix
     */
    public abstract String getPrefix();

    /**
     * Should this ChatMode show up in /chatmode list?
     *
     * @return boolean true if it should show up in /chatmode list
     */
    public boolean isHidden() {
        return false;
    }

    /**
     *
     */
    public String getLoggingFormatString() {
        String name = this.chatModeName == null ? getClass().getSimpleName() : chatModeName;
        return name + ": <%1$s> %2$s";
    }

    public String getLoggingFormatStringNoPlayerTag() {
        String name = this.chatModeName == null ? getClass().getSimpleName() : chatModeName;
        return name + ": %1$s";
    }

    public String getChatFormatString() {
        String prefix = getPrefix();
        return (prefix != null ? prefix + ": " : "") + "[%1$s] %2$s";
    }

    public void logChat(CommandSender sender, String message) {
        String logFormat = getLoggingFormatString();
        if (logFormat != null) {
            String logMessage = String.format(logFormat, sender instanceof Player ? ((Player) sender).getName() : "CONSOLE", message);
            logger.info(logMessage);
        }
    }

    public void logChat(String message) {

        String logFormat = getLoggingFormatString();
        if (logFormat != null) {
            String logMessage = String.format(getLoggingFormatStringNoPlayerTag(), message);
            logger.info(logMessage);
        }
    }
} public class CommandHandler {

    private Map<String, iCommand> commandMap = new HashMap<String, iCommand>();
    private Map<String, Method> newCommandMap = new HashMap<String, Method>();
    private Map<String, Method> aliasCommandMap = new HashMap<String, Method>();
    private Map<String, List<String>> commandAliases = new HashMap<String, List<String>>();
    private Map<Method, Object> instanceMap = new HashMap<Method, Object>();

    /** Command Classes **/

    public AdminCommands adminCommands = null;
    public ChatCommands chatCommands = null;
    public DebugCommands debugCommands = null;
    public EssentialsCommands essentialsCommands = null;
    public GeneralCommands generalCommands = null;
    public TeleportationCommands teleportationCommands = null;
    public WeatherCommands weatherCommands = null;

    /** Bukkit Command Injection stuff **/
    private SimplePluginManager simplePluginManager = null;
    private SimpleCommandMap simpleCommandMap = null;
    private Constructor<PluginCommand> pluginCommandConstructor = null;

    private TweakcraftUtils plugin;

    private Object getMethodInstance(Method method) {
        if (instanceMap.containsKey(method))
            return instanceMap.get(method);

        return null;
    }

    public CommandHandler(TweakcraftUtils instance) {
        this.plugin = instance;
        commandMap.clear();
        newCommandMap.clear();

        adminCommands = new AdminCommands(instance);
        chatCommands = new ChatCommands(instance);
        debugCommands = new DebugCommands(instance);
        essentialsCommands = new EssentialsCommands(instance);
        generalCommands = new GeneralCommands(instance);
        teleportationCommands = new TeleportationCommands(instance);
        weatherCommands = new WeatherCommands(instance);

        addCommandClass(adminCommands, false);
        addCommandClass(chatCommands, false);
        addCommandClass(debugCommands, false);
        addCommandClass(essentialsCommands, false);
        addCommandClass(generalCommands, false);
        addCommandClass(teleportationCommands, false);
        addCommandClass(weatherCommands, false);
    }

    public void addCommandClass(Object instance) {
        this.addCommandClass(instance, true);
    }

    public void addCommandClass(Object instance, boolean injectIntoBukkit) {
        for (Method m : instance.getClass().getDeclaredMethods()) {
            if (m.getAnnotation(aCommand.class) != null)
                injectCommand(m, instance, injectIntoBukkit);
        }
    }


    public void injectCommand(Method method, Object instance, boolean injectIntoBukkit) {
        aCommand annotation = method.getAnnotation(aCommand.class);
        String[] aliases = annotation.aliases();
        if (aliases.length > 0) {

            String commandName = aliases[0];
            String[] commandAliases_array = new String[aliases.length-1];
            if(aliases.length > 1)
                System.arraycopy(aliases, 1, commandAliases_array, 0, aliases.length-1);


            if (newCommandMap.containsKey(commandName)) {
                plugin.getLogger().warning("Duplicate command found!");
                plugin.getLogger().warning("Method : " + method.getName() + "!");
                plugin.getLogger().warning("Command : " + commandName + "!");
                return;
            }
            List<String> al = new ArrayList<String>();
            newCommandMap.put(commandName, method);

            for(String alias : commandAliases_array) {
                al.add(alias);
                aliasCommandMap.put(alias, method);
            }

            this.commandAliases.put(commandName, al);

            instanceMap.put(method, instance);

            if(injectIntoBukkit) {
                // plugin.getLogger().info("Injecting "+commandName+" into Bukkit!");
                this.injectIntoBukkit(commandName, aliases, method, instance, annotation);
            }
        }
    }

    public void getBukkitCommandMap() {
        try {
            Object plmgr = plugin.getServer().getPluginManager();
            if (plmgr instanceof SimplePluginManager) {
                SimplePluginManager pluginmanager = (SimplePluginManager) plmgr;
                Field f = SimplePluginManager.class.getDeclaredField("commandMap");
                f.setAccessible(true);
                SimpleCommandMap simpleCommandMap = (SimpleCommandMap) f.get(pluginmanager);
                Constructor<PluginCommand> con = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
                con.setAccessible(true);
                this.simplePluginManager = pluginmanager;
                this.simpleCommandMap = simpleCommandMap;
                this.pluginCommandConstructor = con;
            }
        } catch (Exception e) {
            plugin.getLogger().warning("Exception caught while getting the CommandMap, plugin command injection will NOT work!");
            e.printStackTrace();
        }

    }

    public void injectIntoBukkit(String command, String[] aliases, Method commandMethod, Object classInstance, aCommand annotation) {

        if(this.simplePluginManager == null)
            this.getBukkitCommandMap();

        try {
            if (this.simplePluginManager != null) {
                PluginCommand cmd = pluginCommandConstructor.newInstance(command, this.plugin);
                cmd.setAliases(Arrays.asList(aliases));
                cmd.setPermission("tweakcraftutils."+annotation.permissionBase());
                cmd.setDescription(annotation.description());
                cmd.setUsage(annotation.usage());

                cmd.setExecutor(plugin);

                this.simpleCommandMap.register("tweakcraftutils", cmd);
            }
        } catch (Exception e) {
            plugin.getLogger().warning("Exception caught while injecting '" + command + "' into Bukkit!");
            e.printStackTrace();
        }
    }

    public List<String> getAliases(String command) {
        return commandAliases.get(command);
    }

    public void addHelpTopics() {

        for(Map.Entry<String, Method> entry : newCommandMap.entrySet()) {

            String commandName = entry.getKey();
            Method m = entry.getValue();

            aCommand annotation = m.getAnnotation(aCommand.class);
            if(annotation == null)
                continue;

            PluginCommand pcmd = plugin.getCommand(commandName);
            if(pcmd!=null && !annotation.permissionBase().equals(""))
                pcmd.setPermission("tweakcraftutils."+annotation.permissionBase());
        }
    }

    @SuppressWarnings("unchecked")
    public void checkCommands() {
        if (plugin.getConfigHandler().enableDebug) { /* Show commands with no command attached */
            PluginDescriptionFile pdFile = plugin.getDescription();
            Map<String, Map<String, Object>> pluginCommands = pdFile.getCommands();
            Set<String> cmds = pluginCommands.keySet();
            for (String c : cmds) {
                if (!this.newCommandMap.containsKey(c) && !this.aliasCommandMap.containsKey(c))
                    plugin.getLogger().warning("WARNING: Unmapped command : " + c);
            }
        }
    }

    public TweakcraftUtils getPlugin() {
        return plugin;
    }

    public Map<String, Method> getCommandMap() {
        return newCommandMap;
    }

    public boolean isInjected(String commandName) {
        return plugin.getCommand(commandName) != null;
        /* for(Map.Entry<String, Method> entry : getCommandMap().entrySet()) {
            if(plugin.getCommand())
        } */
    }

    public Method getCommand(String command) {
        if (newCommandMap.containsKey(command)) {
            return newCommandMap.get(command);
        } else if (aliasCommandMap.containsKey(command)) {
            return aliasCommandMap.get(command);
        } else {
            return null;
        }
    }

    public Object getCommandInstance(Method command) {
        if (instanceMap.containsKey(command)) {
            return instanceMap.get(command);
        } else {
            return null;
        }
    }

    public boolean executeCommand(CommandSender sender, String name, String[] args) {

        String mess = "";
        if (args.length > 1) {
            for (String m : args)
                mess += m + " ";

            mess = mess.substring(0, mess.length() - 1);
        } else if (args.length == 1) {
            mess = args[0];
        }

        // Method

        if (newCommandMap.containsKey(name) || aliasCommandMap.containsKey(name)) {
            try {
                if (!runMethod(sender, name, args)) {
                    sender.sendMessage("This command did not go as intended!");
                }
                /* if (sender instanceof Player) {
                    final LocalPlayer lp = plugin.wrapPlayer((Player)sender);
                    plugin.getLogger().info((lp.isInvisible()?"[INVIS] ":"") + sender.getName() + " issued: /" + name + " " + mess);
                } else */
                if(!(sender instanceof Player))
                    plugin.getLogger().info("CONSOLE issued: /" + name + " " + mess);
                return true;
            } /*catch (CommandNotFoundException e) {
                    sender.sendMessage("TweakcraftUtils error, command not found!");
                }*/ catch (PermissionsException e) {
                sender.sendMessage(ChatColor.RED + "You do not have the correct permissions for this command or usage!");
                if (sender instanceof Player) {
                    final LocalPlayer lp = plugin.wrapPlayer((Player)sender);
                    plugin.getLogger().info((lp.isInvisible()?"[INVIS] ":"") + ((Player) sender).getName() + " tried: /" + name + " " + mess);
                }
            } catch (CommandUsageException e) {
                sender.sendMessage(ChatColor.YELLOW + e.getMessage());
            } catch (CommandSenderException e) {
                sender.sendMessage(ChatColor.YELLOW + e.getMessage());
            } catch (CommandException e) {
                sender.sendMessage(ChatColor.YELLOW + e.getMessage());
                plugin.getLogger().info((sender instanceof Player ? ((Player) sender).getName() : "CONSOLE") + " got a CommandException on : /" + name + " " + mess);
            }
        }
        return false;
    }

    public boolean runMethod(CommandSender sender, String name, String[] args)
        throws PermissionsException, CommandSenderException, CommandUsageException, CommandException {

        String argsString = "";
        for(String a : args) {
            argsString+= a+" ";
        }

        try {
            Method command = getCommand(name);
            return (Boolean) command.invoke(getMethodInstance(command), sender, name, args);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof PermissionsException)
                throw (PermissionsException) t;
            if (t instanceof CommandSenderException)
                throw (CommandSenderException) t;
            if (t instanceof CommandUsageException)
                throw (CommandUsageException) t;
            if (t instanceof CommandException)
                throw (CommandException) t;

            plugin.getLogger().warning("Error occured while executing command!");
            plugin.getLogger().warning("Errornous command : " + name + " " + argsString + "!");
            plugin.getLogger().warning("CommandSender : " + (sender instanceof Player ? ((Player) sender).getName() : "CONSOLE"));
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            plugin.getLogger().warning("Error occured while executing command!");
            plugin.getLogger().warning("Errornous command : " + name + " " + argsString + "!");
            plugin.getLogger().warning("CommandSender : " + (sender instanceof Player ? ((Player) sender).getName() : "CONSOLE"));
            e.printStackTrace();
        }

        return false;
    }
} public class ConfigurationHandler {

    private File globalconfigFile;
    private File seenconfigFile;

    private YamlConfiguration globalconfig = new YamlConfiguration();
    private YamlConfiguration seenconfig = new YamlConfiguration();
    private TweakcraftUtils plugin;

    private Map<String, Map<Integer, Boolean>> lsbindmap;
    private Map<String, LockdownLocation> lockdowns;
    private Map<String, List<Location>> tpfromlocations;

    private boolean loadSuccessful = true;

    /**
     * Defaults
     */
    public boolean enableSeenConfig = false;
    public boolean enableWorldGuard = false;
    public boolean enableZones = false;
    public boolean enableLogBlock = false;
    public boolean enableIRC = false;
    public boolean NoLaggEnabled = false;
    public boolean enableKickMessage = false;
    public boolean enableTPBack = true;
    public boolean enableGroupChat = true;
    public boolean enableLocalChat = true;
    public boolean enableWorldChat = true;
    public int localchatdistance = 200;
    public Integer helpPerPage = 10;
    public List<String> extrahelpplugin = new ArrayList<String>();
    public List<String> extrahelphide = new ArrayList<String>();
    public boolean enabletamertool = true;
    public int     tamertoolid = Material.STICK.getId();
    public boolean enablePersistence = true;
    public boolean useTweakBotSeen = false;
    public String  AIRCtag = "mchatadmin";
    public String  GIRCtag = "mchat";
    public String  LoggingIRCtag = "mchatlog";
    public String  AIRCMessageFormat = "[A] <%name%> %message%";
    public String  GIRCMessageFormat = "<%name%> %message%";
    public String  LoggingIRCMessageFormat = "<%name%> %message%";
    public boolean cancelNickChat = true;
    public boolean LoggingIRCenabled = true;
    public boolean GIRCenabled = true;
    public boolean AIRCenabled = false;
    public boolean enableDebug = false; /* Verbose messages */
    public boolean enableAutoTame = false;
    public boolean paySaddle = true;
    public boolean stopChunkUnloadBurningFurnace = false;
    public boolean enableCUI = false;
    public boolean enablemod_InfDura = false;
    public boolean enableExperienceOrbsHalt = false;
    public boolean enableTargetIgnoreAFKPlayers = false;
    public boolean enableWorldMOTD = false;
    public boolean enableTNTArrows = false;
    public float tntArrowForce = 4.0F;
    public boolean enableTweakTravel = false;
    public boolean enableSnowPile = false;
    public int snowPileRate = 2;
    public int snowPileMaxHeight = 7;
    public boolean enableSnowDouble = false;
    public int tweakTravelSearchWidth = 3;
    public boolean enableVanish = false;

    public boolean enableFallDistanceNullify = false;

    // public PermissionsResolver.PermissionResolvingMode permissoinsResolvingMode = null;

    public boolean enableRespawnHook = false;
    public boolean enableRespawnHeal = false;
    // public int     portalSearchRadius = 128;

    public boolean enableSpamControl = false;
    public long spamCheckTime = 500L;
    public int spamMuteMinutes = 5;
    public int spamMaxMessages = 5;
    public String spamMuteMessage = "{name} has been auto-muted for spamming!";


    /**
     * EXTRA/TEMPORARY STUFF
     */
    public boolean pigRecoverSaddle = true;
    public boolean stopIgniteWorldGuard = true;
    // public Map<String, String>
    public boolean cancelNetherPortal = false;
    public boolean extraLogging = false;
    public boolean enableInjectedCommandsHanding = false;

    public ConfigurationHandler(TweakcraftUtils instance) {
        this.plugin = instance;
        lsbindmap = new HashMap<String, Map<Integer, Boolean>>();
        lockdowns = new HashMap<String, LockdownLocation>();
    }

    public void reloadConfig() {
        if (!plugin.getDataFolder().exists()) {
            plugin.getDataFolder().mkdirs();
        }
        // if(this.globalconfig==null) this.globalconfig = (YamlConfiguration) plugin.getConfig();
        if (!globalconfigFile.exists()) {
            plugin.getLogger().warning("No config file found, using default values!");
            return;
        }
        try {
            globalconfig.load(globalconfigFile);
        } catch (InvalidConfigurationException e) {
            plugin.getLogger().warning("InvalidConfigurationException while loading config file, using old values!");
            e.printStackTrace();
            return;
        } /* catch (FileNotFoundException e) {
            plugin.getLogger().warning("No config file found, using default values!");
            e.printStackTrace();
            return;
        } */ catch (IOException e) {
            plugin.getLogger().warning("IOException while loading config file, using old values!");
            e.printStackTrace();
            return;
        }
        // plugin.reloadConfig();

        this.plugin.getLogger().info("Parsing configuration file...");
        this.plugin.getLogger().info("Config : "+globalconfigFile.getAbsolutePath());

        this.enablePersistence = globalconfig.getBoolean("Persistence.enabled", true);
        this.useTweakBotSeen = globalconfig.getBoolean("Persistence.useTweakBotSeen", false);
        plugin.getLogger().info("Using TweakBot's seen table for /seen!");

        this.enableLocalChat = globalconfig.getBoolean("ChatMode.LocalChat.enabled", true);
        this.localchatdistance = globalconfig.getInt("ChatMode.LocalChat.range", 200);
        this.enableWorldChat = globalconfig.getBoolean("ChatMode.WorldChat.enabled", true);
        this.enableWorldGuard = globalconfig.getBoolean("ChatMode.RegionChat.enabled", false);
        this.enableZones = globalconfig.getBoolean("ChatMode.ZoneChat.enabled", false);
        this.enableIRC = globalconfig.getBoolean("CraftIRC.enabled", false);
        this.AIRCMessageFormat = globalconfig.getString("CraftIRC.admin.MessageFormat");
        this.AIRCenabled = globalconfig.getBoolean("CraftIRC.admin.enabled", false);
        this.AIRCtag = globalconfig.getString("CraftIRC.admin.tag", "mchatadmin");
        this.GIRCMessageFormat = globalconfig.getString("CraftIRC.regular.MessageFormat");
        this.GIRCtag = globalconfig.getString("CraftIRC.regular.tag", "mchat");
        this.GIRCMessageFormat = globalconfig.getString("CraftIRC.regular.MessageFormat");
        this.LoggingIRCenabled = globalconfig.getBoolean("CraftIRC.log.enabled", true);
        this.LoggingIRCtag = globalconfig.getString("CraftIRC.log.tag", "mchatlog");
        this.LoggingIRCMessageFormat = globalconfig.getString("CraftIRC.log.MessageFormat");
        this.enableTPBack = globalconfig.getBoolean("enableTPBack", true);
        this.enableDebug = globalconfig.getBoolean("debug.enabled", false);
        if(this.enableDebug) {
            plugin.getLogger().info("Extra verbose messages enabled!");
        }

        this.enableRespawnHook = globalconfig.getBoolean("respawn.enableHook", false);
        this.enableRespawnHeal = globalconfig.getBoolean("respawn.healOnRespawn", false);

        this.extrahelpplugin = new ArrayList<String>();
        this.enableGroupChat = globalconfig.getBoolean("ChatMode.GroupChat", true);

        List<String> extralist = globalconfig.getStringList("extrahelp.plugins");
        if(extralist!=null) {
            for(String plist : extralist) {
                if(plugin.getServer().getPluginManager().getPlugin(plist) != null) {
                    if(!extrahelpplugin.contains(plist)) {
                        if(this.enableDebug)
                            plugin.getLogger().info("Adding "+plist+" to the /help addons.");
                        extrahelpplugin.add(plist);
                    } else {
                        plugin.getLogger().info("WARNING: "+plist+" is on the extrahelp list multiple times!");
                    }
                } else {
                    plugin.getLogger().info("WARNING: Can't find plugin with name "+plist+"! Not adding to the help list.");
                }
            }
        }

        this.cancelNetherPortal = globalconfig.getBoolean("worlds.cancelportal", false);

        this.extrahelphide = globalconfig.getStringList("extrahelp.hide");

        if (globalconfig.getBoolean("PlayerHistory.enabled", false)) {
            plugin.getLogger().info("Keeping player history!");
            File seenFile = new File(plugin.getDataFolder(), "players.yml");
            try {
                this.seenconfig.load(seenFile);
                this.enableSeenConfig = true;
            } catch (IOException e) {
                plugin.getLogger().severe("IOExeption thrown while trying to load the seen.yml file! playerhistory disabled!");
                this.enableSeenConfig = false;
            } catch (InvalidConfigurationException e) {
                plugin.getLogger().severe("InvalidConfigurationException thrown while trying to load the seen.yml file! playerhistory disabled!");
                this.enableSeenConfig = false;
            }

        }

        this.enabletamertool =  globalconfig.getBoolean("tamer.enabled", true);
        this.tamertoolid = globalconfig.getInt("tamer.toolid", Material.STICK.getId());
        this.enableAutoTame = globalconfig.getBoolean("mount.autotame", false);
        this.paySaddle = globalconfig.getBoolean("mount.paysaddle", true);
        this.stopChunkUnloadBurningFurnace = globalconfig.getBoolean("extra.stopChunkUnloadBurningFurnace", false);
        this.pigRecoverSaddle = globalconfig.getBoolean("extra.recoverPigSaddle", true);
        this.stopIgniteWorldGuard = globalconfig.getBoolean("extra.stopGodIgnite", false);
        this.enableCUI = globalconfig.getBoolean("extra.CUI", false);
        this.enablemod_InfDura = globalconfig.getBoolean("extra.mod_InfDura", false);
        this.enableExperienceOrbsHalt = globalconfig.getBoolean("extra.stopExperienceOrbs", false);
        this.enableTargetIgnoreAFKPlayers = globalconfig.getBoolean("extra.targetIgnoreAFK", false);
        this.cancelNickChat = globalconfig.getBoolean("extra.cancelNickChat", true);
        this.extraLogging = globalconfig.getBoolean("extra.extraLogging", false);
        this.enableTNTArrows = globalconfig.getBoolean("extra.tntArrows.enabled", false);
        this.tntArrowForce = (float) globalconfig.getDouble("extra.tntArrows.force", 4.0F);
        this.enableLogBlock =  globalconfig.getBoolean("extra.tntArrows.logblock", false);
        this.enableFallDistanceNullify = globalconfig.getBoolean("extra.nullifyTeleportFallDistance", false);
        this.enableSnowPile = globalconfig.getBoolean("extra.snowpile.enabled", false);
        this.snowPileRate = globalconfig.getInt("extra.snowpile.rate", 2500);
        this.snowPileMaxHeight = globalconfig.getInt("extra.snowpile.maxHeight", 7);
        this.snowPileMaxHeight = Math.max(this.snowPileMaxHeight, 7);
        this.enableSnowDouble = globalconfig.getBoolean("extra.snowpile.doublesnowball", false);
        this.enableInjectedCommandsHanding = globalconfig.getBoolean("extra.enableInjectedCommandsHanding", false);
        this.enableVanish = globalconfig.getBoolean("invisible.useVanish", false);
        this.enableKickMessage = globalconfig.getBoolean("extra.enableKickMessage", false);



        this.enableWorldMOTD = globalconfig.getBoolean("worlds.worldMOTD", false);
        this.enableTweakTravel = globalconfig.getBoolean("worlds.TweakTravel", false);
        this.tweakTravelSearchWidth = globalconfig.getInt("worlds.TweakTravelSearchWidth", 3);

        this.enableSpamControl = globalconfig.getBoolean("spamcontrol.enable", false);
        if(this.enableSpamControl) {
            plugin.getLogger().info("Enabling spam control!");
            plugin.getChathandler().enableAntiSpam();
        }
        this.spamCheckTime = globalconfig.getInt("spamcontrol.checkTime", 5)*100;
        this.spamMuteMinutes = globalconfig.getInt("spamcontrol.muteTime", 5);
        this.spamMaxMessages = globalconfig.getInt("spamcontrol.maxMessages", 5);
        this.spamMuteMessage = globalconfig.getString("spamcontrol.muteMessage", "{name} has been auto-muted for spamming!");

        if(this.enablePersistence) {
            plugin.getPlayerListener().reloadInfo();
        }
    }

    public YamlConfiguration getGlobalconfig() {
        return globalconfig;
    }

    public boolean isEnableZones() {
        return enableZones;
    }

    public boolean isEnableWorldGuard() {
        return enableWorldGuard;
    }

    public int getLocalchatdistance() {
        return localchatdistance;
    }

    public boolean isEnableSeenConfig() {
        return enableSeenConfig;
    }

    public YamlConfiguration getSeenconfig() {
        return seenconfig;
    }

    public Map<String, Map<Integer, Boolean>> getLsbindmap() {
        return lsbindmap;
    }

    public Map<String, LockdownLocation> getLockdowns() {
        return lockdowns;
    }

    public String getDirSeperator() {
        return System.getProperty("file.separator");
    }

    public void saveSeenConfig() throws IOException {
        if(seenconfig!=null) {
            seenconfig.save(seenconfigFile);
        }
    }

    public void setGlobalConfig(File file) {
        this.globalconfigFile = file;
        this.seenconfigFile = new File(file.getParentFile(), "seen.yml");
    }
} public class BanHandler {

    private Map<UUID, Ban> bans;
    private TweakcraftUtils plugin;

    private final String BANNED_SECTION = "banned-players";

    public BanHandler(TweakcraftUtils instance) {
        this.plugin = instance;
        /**
         * This is handled later on with reloadBans();
         */
        // loadBans();
    }

    public Ban searchBan(String playername) {
        //Ban ban = null;
        for (Map.Entry<UUID, Ban> entryMap : bans.entrySet()) {
            if (entryMap.getValue().getPlayer().equalsIgnoreCase(playername)) {
                return entryMap.getValue();
            }
        }
        return null; // I Has found nothing!
    }

    private String findOldBan(Map<String, Ban> oldBanMap, String playerName) {
        for(Map.Entry<String, Ban> banEntry : oldBanMap.entrySet()) {
            if(playerName.equalsIgnoreCase(banEntry.getKey()))
                return banEntry.getKey();
        }
        return null;
    }

    private void convertOldBanFile(File oldBanFile, YamlConfiguration newBanConfig) {

        Map<String, Ban> oldBans = new HashMap<String, Ban>();

        try {
            // File banfile = new File(plugin.getDataFolder(), "banned-players.txt");

            Ban.skipUUIDCheck = true;
            BufferedReader banfilereader = new BufferedReader(new FileReader(oldBanFile));
            String line = "";
            while ((line = banfilereader.readLine()) != null) {
                if (!line.trim().equals("")) {
                    String[] lin = line.split(",");
                    if (lin.length > 1)
                        oldBans.put(lin[0], new Ban(lin[0], lin[1]));
                    else
                        oldBans.put(lin[0], new Ban(lin[0], ""));
                }
            }
            banfilereader.close();

            Map<String, UUID> uuids = new HashMap<String, UUID>();

            try {
                if(Class.forName("com.evilmidget38.UUIDFetcher") != null) {
                    List<String> bannedNames = new ArrayList<String>(oldBans.keySet());
                    com.evilmidget38.UUIDFetcher fetcher = new com.evilmidget38.UUIDFetcher(bannedNames, true);
                    uuids = fetcher.call();
                }

            } catch( Exception e ) {
                /* Do it the slow way */

                plugin.getLogger().warning("Didn't find evilmidgets' UUIDFetcher, doing the lookup the slow way.");
                for (Map.Entry<String, Ban> banEntry : oldBans.entrySet()) {
                /* Check for UUID, just remove the play if we didn't find any (non-existant profile) */
                    OfflinePlayer op = Bukkit.getOfflinePlayer(banEntry.getKey());
                    UUID uuid = op.getUniqueId();
                    uuids.put(op.getName(), op.getUniqueId());
                }
            }

            Ban.skipUUIDCheck = false;

            // Go over the list again because names might not be in the correct case.
            for(Map.Entry<String, UUID> uuidEntry : uuids.entrySet()) {
                String findBan = findOldBan(oldBans, uuidEntry.getKey());
                if(!(findBan.equals(uuidEntry.getKey()))) {
                    Ban ban = oldBans.get(findBan);
                    ban.setPlayer(uuidEntry.getKey());
                    oldBans.remove(findBan);
                    oldBans.put(uuidEntry.getKey(), ban);
                }
            }

            for (Map.Entry<String, Ban> banEntry : oldBans.entrySet()) {
                UUID uuid = uuids.get(banEntry.getKey());
                if (uuid == null) {
                    plugin.getLogger().warning("No Mojang profile found for " + banEntry.getValue().getPlayer() + ", ignoring line!");
                } else {
                    Ban ban = banEntry.getValue();
                    String playerName = ban.getPlayer();
                    Long toTime = ban.getToTime();
                    String reason = ban.getReason();

                    String section = BANNED_SECTION + "." + uuid;
                    newBanConfig.createSection(section);
                    newBanConfig.set(section + ".playername", playerName);
                    newBanConfig.set(section + ".reason", reason);
                    if (toTime != null) {
                        newBanConfig.set(section + ".toTime", toTime);
                    }
                }
            }


            boolean deleted = oldBanFile.delete();
            if(!deleted) plugin.getLogger().warning("Could not remove old banfile!");

            plugin.getLogger().info("Converted banned players file to newer format.");

        } catch (FileNotFoundException e) {
            plugin.getLogger().warning("Ban file not found!");
        } catch (IOException e) {
            plugin.getLogger().info("Ban file I/O error!");
        }
    }

    private void loadBans() {

        bans = new HashMap<UUID, Ban>();

        File banFile = new File(plugin.getDataFolder(), "banned-players.yml");
        if(!banFile.exists()) {
            try {
                banFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        YamlConfiguration banConfig = new YamlConfiguration();
        try {
            banConfig.load(banFile);
        } catch(Exception e) {
            plugin.getLogger().warning("Error while loading banned-players.yml, does this file exist?");
        }

        // check for old BanFile
        File oldBanFile = new File(plugin.getDataFolder(), "banned-players.txt");
        if (oldBanFile.exists()) {
            plugin.getLogger().warning("Old banned-players file found, converting to new format!");
            this.convertOldBanFile(oldBanFile, banConfig);
            try {
                banConfig.save(banFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        ConfigurationSection section = banConfig.getConfigurationSection(BANNED_SECTION);
        if (section != null) {
            Set<String> bannedUUIDSet = section.getKeys(false);
            for (String b : bannedUUIDSet) {
                UUID uuid = UUID.fromString(b);
                String playerName = banConfig.getString(BANNED_SECTION + "." + b + ".playername");
                String reason = banConfig.getString(BANNED_SECTION + "." + b + ".reason");

                Long time = banConfig.getLong(BANNED_SECTION + "." + b + ".toTime", 0L);
                if (time == 0L) time = null;

                Ban ban = new Ban(playerName, uuid, reason, time);
                bans.put(uuid, ban);
            }
        }


        if(plugin.getConfigHandler().enablePersistence) {
            List<PlayerOptions> popts = plugin.getDatabase(). find(PlayerOptions.class).where().ieq("optionname", "ban").findList();
            for(PlayerOptions po : popts) {
                PlayerOptions tmppo = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", po.getName()).ieq("optionname", "banmsg").findUnique();
                String reason = "";
                if(tmppo!=null)
                    reason = tmppo.getOptionvalue();
                Long time = null;
                try{
                    time = Long.parseLong(po.getOptionvalue());
                } catch(NumberFormatException ex) { }

                boolean fakeplayer = false;

                PlayerOptions poUUID = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", po.getName()).ieq("optionname", "uuid").findUnique();
                if(poUUID == null) {

                    OfflinePlayer op = Bukkit.getServer().getOfflinePlayer(po.getName());
                    UUID uuid = op.getUniqueId();
                    if (uuid == null) {
                        plugin.getLogger().warning("Found a banned player in the database without a GameProfile, does this user exist? "+po.getName());
                        fakeplayer = true;
                    } else {
                        poUUID = new PlayerOptions();
                        poUUID.setName(po.getName());
                        poUUID.setOptionname("uuid");
                        poUUID.setOptionvalue(uuid.toString());
                        plugin.getDatabase().save(poUUID);
                    }
                    if(!fakeplayer) bans.put(UUID.fromString(poUUID.getOptionvalue()), new Ban(po.getName(), reason, time));
                }

            }
        }

        // return banlist;
    }

    public boolean isBanned(UUID uuid) {
        boolean banned = false;

        Ban b = bans.get(uuid);
        // Ban b = searchBan(playername);

        if (b != null) {
            if (b.getToTime() == null) {
                banned = true;
            } else {
                Long curTime = Calendar.getInstance().getTime().getTime();
                if (curTime < b.getToTime()) {
                    if (plugin.getConfigHandler().enableDebug) {
                        Long toTime = b.getToTime();
                        Double timerem = Math.floor((toTime - curTime) / 1000);
                        String toGo = TimeTool.calcLeft(timerem.longValue());
                        plugin.getLogger().info("Bans: " + b.getPlayer() + " still has " + toGo + " to go!");
                    }
                    banned = true;
                } else {
                    plugin.getLogger().info("Bans: auto-unbanning " + b.getPlayer() + ", his bantime was over!");
                    if (plugin.getConfigHandler().enableDebug) {
                        Long toTime = b.getToTime();
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

                        plugin.getLogger().info("Bans: it expired at " + sdf.format(new Date(toTime)) + "!");
                    }
                    banned = false;
                    unBan(b.getPlayer());
                }
            }
        }
        return banned;
    }

    public boolean isBanned(String playername) {
        Ban b = searchBan(playername);
        return b != null && isBanned(b.getUUID());
    }

    public String getRemainingTime(String playername) {
        if(this.isBanned(playername)) {
            Ban b = isBannedBan(playername);
            if(b.getToTime()==null) {
                return "forever";
            } else {
                Long curTime = Calendar.getInstance().getTime().getTime();
                Long toTime = b.getToTime();
                Double timerem = Math.floor((toTime-curTime)/1000);
                return TimeTool.calcLeft(timerem.longValue());
            }
        } else {
            return null;
        }
    }

    public String getReason(String playername) {
        Ban b = this.isBannedBan(playername);
        if(b!=null) {
            return b.getReason();
        } else {
            return null;
        }
    }

    public Ban isBannedBan(String playername) {
        if(isBanned(playername))
            return searchBan(playername);
        else
            return null;
    }

    public Ban isBannedBan(UUID uuid) {
        return bans.get(uuid);
        // return null;
    }

    public boolean isBannedFullname(String playername) {
        return bans.containsKey(playername);
    }

    public boolean banPlayer(UUID uuid, String playername, String reason) {
        if (playername.trim().equals("")) {
            plugin.getLogger().info("Can't ban an empty player!");
        } else {
            bans.put(uuid, new Ban(playername, uuid, reason, null));
            if(plugin.getConfigHandler().enablePersistence) {
                PlayerOptions po = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", playername).ieq("optionname", "ban").findUnique();
                if(po==null) {
                    po = new PlayerOptions();
                    po.setName(playername);
                    po.setOptionname("ban");
                }
                plugin.getDatabase().save(po);
            }
            return true;
        }
        return false;
    }

    public boolean banPlayer(UUID uuid, String player, String reason, Long duration) {
        if(duration == null) { return banPlayer(uuid, player.toLowerCase(), reason); } else {
            String playername = player.toLowerCase();
            Long toTime = null;
            toTime  = Calendar.getInstance().getTime().getTime();
            toTime += duration*1000;
            if (playername.trim().equals("")) {
                plugin.getLogger().info("Can't ban an empty player!");
                return false;
            } else {
                bans.put(uuid, new Ban(playername, uuid, reason, toTime));
                // if(plugin.getConfigHandler().enablePersistence) /* Don't need this, CommandBan already checks this! */
                PlayerOptions po = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", playername).ieq("optionname", "ban").findUnique();
                if(po==null) {
                    po = new PlayerOptions();
                    po.setName(playername);
                    po.setOptionname("ban");
                }
                po.setOptionvalue(toTime.toString());
                plugin.getDatabase().save(po);
                /* Now we have to save the reason! */
                PlayerOptions banmsg = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", playername).ieq("optionname", "banmsg").findUnique();
                if(banmsg==null) {
                    banmsg = new PlayerOptions();
                    banmsg.setName(playername);
                    banmsg.setOptionname("banmsg");
                }
                
                banmsg.setOptionvalue(reason);
                plugin.getDatabase().save(banmsg);
                return true;
            }
        }
    }

    public Map<UUID, Ban> getBans() {
        return bans;
    }

    public void unBan(String player) {
        OfflinePlayer p = Bukkit.getOfflinePlayer(player);
        UUID uuid = p.getUniqueId();
        if(bans.containsKey(uuid))
            bans.remove(uuid);

        if(plugin.getConfigHandler().enablePersistence) {
            List<PlayerOptions> popts = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", player).in("optionname", "ban", "banmsg").findList();
            for(PlayerOptions po : popts)
                plugin.getDatabase().delete(po);
        }

    }

    /**
     * This recreates banned-player.txt with the list that is loaded in memory
     *
     * This excludes the persistent (non-permament bans) ones. They are saved automatically
     * as well so we won't need something like this.
     */
    public void saveBans() {

        File banFile = new File(plugin.getDataFolder(), "banned-players.yml");
        if (!banFile.exists()) {
            try {
                banFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        YamlConfiguration banConfig = new YamlConfiguration();
        banConfig.createSection(BANNED_SECTION);
        for(Map.Entry<UUID, Ban> banEntry : bans.entrySet()) {
            Ban ban = banEntry.getValue();
            String playerName = ban.getPlayer();
            Long toTime = ban.getToTime();
            String reason = ban.getReason();

            String section = BANNED_SECTION+"."+banEntry.getKey().toString();
            banConfig.createSection(section);
            banConfig.set(section + ".playername", playerName);
            banConfig.set(section+".reason", reason);
            if(toTime != null) {
                banConfig.set(section+".toTime", toTime);
            }
        }
        try {
            banConfig.save(banFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void reloadBans() {
        this.loadBans();
        plugin.getLogger().info("Loaded banlist, " + bans.size() + " bans and counting!");
    }
} @Entity()
@Table(name="seen")
public class PlayerHistoryInfo {
    
    @Id
    public Integer id;

    @NotNull
    @Length(max=25)
    public String nickname;

    @NotNull
    public String text;

    @NotNull
    @Length(max=25)
    public String act;

    @NotNull
    @Length(max=25)
    public String channel;

    @Temporal(TemporalType.TIMESTAMP)
    public Date date;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public String getAct() {
        return act;
    }

    public void setAct(String act) {
        this.act = act;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
} @Entity()
@Table(name="tcutils_players")
public class PlayerInfo {
    @Id
    public int Id;

    @NotNull
    public String name;

    public String nick;
    
    public Long lastseen;

    public int getId() {
        return Id;
    }

    public void setId(int id) {
        Id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getLastseen() {
        return lastseen;
    }

    public void setLastseen(Long lastseen) {

        this.lastseen = lastseen;
    }

    public String getNick() {
        return nick;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }
} @Entity
@Table(name="tcutils_options")
public class PlayerOptions {

    @Id
    private int id;

    @NotNull
    @Length(max=25)
    private String name;

    @NotNull
    @Length(max=35)
    private String optionname;

    private String optionvalue;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getOptionname() {
        return optionname;
    }

    public void setOptionname(String option) {
        this.optionname = option;
    }

    public String getOptionvalue() {
        return optionvalue;
    }

    public void setOptionvalue(String optionvalue) {
        this.optionvalue = optionvalue;
    }
} public class TweakcraftUtilsEvent extends Event {

	protected Action action;
    private LocalPlayer player;
	private static final HandlerList handlers = new HandlerList();

	public TweakcraftUtilsEvent(Action action) {
		this.action = action;
	}

    public LocalPlayer getPlayer() {
        return player;
    }

    public void setPlayer(LocalPlayer player) {
        this.player = player;
    }

    public void setPlayer(Player player) {
        TweakcraftUtils.getInstance().wrapPlayer(player);
    }

    public Action getAction() {
		return this.action;
	}

	@Override
	public HandlerList getHandlers() {
		return handlers;
	}

	public static HandlerList getHandlerList() {
		return handlers;
	}

	public enum Action {
        CHATMODE_CHANGED,
        NICK_CHANGED,
        RELOAD_INFO,
        RELOAD
	}
} public class CraftIRCAdminEndPoint implements EndPoint {

    private TweakcraftUtils plugin = null;
    private ChatHandler ch = null;
    private ChatMode cm = null;
    private AdminChat ac = null;
    private CraftIRC circ = null;

    public CraftIRCAdminEndPoint(TweakcraftUtils instance) {
        this.plugin = instance;
        this.ch = instance.getChathandler();
        circ = instance.getCraftIRC();
        circ.registerEndPoint("tcutilsadmin", this);
        // this.cm = ch.getChatMode("A")
        try {
            this.ac = (AdminChat) ch.getChatMode("admin");
        } catch (ChatModeException ex) {
        }
    }

    public void messageIn(RelayedMessage relayedMessage) {
        /* chat: '%grey%[IRC]%foreground% <%ircPrefix%%sender%> %message%'
      private: '%grey%[IRC]%foreground% %sender% whispers> %message%'
      action: '%grey%[IRC]%purple% * %ircPrefix%%sender% %message%'
      join: '%grey%[IRC]%darkgreen% * Joins: %sender%'
      part: '%grey%[IRC]%darkgreen% * Parts: %sender%'
      quit: '%grey%[IRC]%blue% * Quits: %sender%'
      kick: '%grey%[IRC]%darkgreen% * %sender% was kicked by %ircModPrefix%%moderator%'
      nick: '%grey%[IRC]%darkgreen% * %sender% is now known as %message%'
      generic: '%grey%%message%' */

        String event = relayedMessage.getEvent();
        if (event.equals("chat") || event.equals("action")) {
            String pname = relayedMessage.getField("sender");
            if (pname != null) {
                String name = relayedMessage.getField("sender");
                String nick = plugin.getPlayerListener().findPlayerNameByNick(name, true);
                relayedMessage.setField("sender", plugin.getNickWithColors(nick == null ? name : nick));
            } else {
                relayedMessage.setField("sender", "unknownSender");
            }
            ac.broadcastMessage(relayedMessage.getMessage(this));
        }

    }

    public Type getType() {
        return EndPoint.Type.MINECRAFT;
    }

    public boolean userMessageIn(String s, RelayedMessage relayedMessage) {
        return false;
    }

    public boolean adminMessageIn(RelayedMessage relayedMessage) {
        return false;
    }

    public List<String> listUsers() {
        return null;
    }

    public List<String> listDisplayUsers() {
        return null;
    }
} public class CraftIRCEndPoint implements EndPoint {

    private ChatHandler ch = null;
    private CraftIRC circ = null;

    public CraftIRCEndPoint(TweakcraftUtils instance, String tag) {
        this.ch = instance.getChathandler();
        this.circ = instance.getCraftIRC();
        circ.registerEndPoint(tag, this);
    }

    public Type getType() {
        return EndPoint.Type.MINECRAFT;
    }

    public void messageIn(RelayedMessage relayedMessage) {
        /* String event = relayedMessage.getEvent();
        if(event.equals("chat") || event.equals("action")) {
            String pname = relayedMessage.getField("sender");
            if(pname!=null) {
                String name = relayedMessage.getField("sender");
                String nick = plugin.getPlayerListener().findPlayerNameByNick(name, true);
                relayedMessage.setField("sender", plugin.getNickWithColors(nick==null?name:nick));
            }
            ac.broadcastMessage(relayedMessage.getMessage(this));
        } */
    }

    public boolean userMessageIn(String s, RelayedMessage relayedMessage) {
        return false;
    }

    public boolean adminMessageIn(RelayedMessage relayedMessage) {
        return false;
    }

    public List<String> listUsers() {
        return null;
    }

    public List<String> listDisplayUsers() {
        return null;
    }
} public class ItemDB {

    private TweakcraftUtils plugin;
    private Map<String, Item> itemmap;

    public ItemDB(TweakcraftUtils instance) {
        this.plugin = instance;
    }

    public void loadDataBase() {
        itemmap = new HashMap<String, Item>();
        File itemfile = new File(plugin.getDataFolder(), "items.csv");

        try {
            BufferedReader reader = new BufferedReader(new FileReader(itemfile));
            String itemline = "";
            Integer line = 1;
            while ((itemline = reader.readLine()) != null) {
                if (!itemline.startsWith("#")) {
                    try {
                        String[] split = itemline.split(",");
                        String itemname = split[0];
                        Integer itemnr = Integer.parseInt(split[1]);
                        Byte damage = Byte.parseByte(split[2]);
                        Integer stacks = Integer.parseInt(split[3]);
                        itemmap.put(itemname, new Item(itemnr, damage, stacks));
                    } catch (Throwable e) {
                        plugin.getLogger().info("Item error at line " + line);
                    }
                }
                line++;
            }
        } catch (FileNotFoundException e) {
            plugin.getLogger().info("Item DB not found!");
            plugin.getLogger().info("looked in " + plugin.getDataFolder());
        } catch (IOException e) {
            plugin.getLogger().info("Item DB IO error!");
        }
        plugin.getLogger().info("Loaded item DB, " + itemmap.size() + " items found!");
    }

    public Item getItem(String name) {
        if (itemmap.containsKey(name)) {
            return itemmap.get(name);
        } else {
            // try WorldEdit first!
            if(plugin.getWorldEdit()!=null) {
                ItemType it = ItemType.lookup(name, true);
                if(it!=null) {
                    Item item = new Item(it.getID(), (byte) 0, 64);
                    // Let's add that to the cache, for performance reasons
                    itemmap.put(name, item);
                    return item;
                }
            }
            return null;
        }
    }

} public class LocalPlayer {

    private String name;
    private UUID uuid;
    private Player bukkitPlayer;
    private OfflinePlayer bukkitOfflinePlayer;
    private ChatMode chatMode;
    private boolean afk;
    private int spamcounter;
    private long lastmessagetime;
    private String nick = null;
    private boolean tpOff = false;
    private boolean invisible = false;
    private boolean invisiblePickup = false;
    private String replyTo = null;
    private boolean tntArrow = false;
    private boolean allowedFlight = false;
    private String capeURL = null;
    private String skinURL = null;

    public boolean isInvisible() {
        return this.invisible;
    }

    public boolean isAllowedFlight() {
        return allowedFlight;
    }

    public void setAllowedFlight(boolean hasFlight) {
        this.allowedFlight = hasFlight;
        if(getBukkitPlayer()!=null)
            getBukkitPlayer().setAllowFlight(hasFlight);
    }

    public UUID getUuid() {
        return uuid;
    }

    public void setUuid(UUID uuid) {
        this.uuid = uuid;
    }

    public void setInvisible(boolean state) {
        this.invisible = state;
    }

    public boolean isTpOff() {
        return tpOff;
    }

    public void setTpOff(boolean state) {
        this.tpOff = state;
    }

    public void setInvisiblePickup(boolean state) {
        this.invisiblePickup = state;
    }

    public boolean isTntArrow() {
        return tntArrow;
    }

    public void setTntArrow(boolean tntArrow) {
        this.tntArrow = tntArrow;
    }

    public boolean hasNick() {
        return nick != null;
    }

    public String getNick() {
        return this.nick;
    }

    public String getReplyTo() {
        return replyTo;
    }

    public void setReplyTo(String replyTo) {
        this.replyTo = replyTo;
    }

    public void setNick(String nick) {
        this.nick = nick;
    }

    public OfflinePlayer getBukkitOfflinePlayer() {
        OfflinePlayer op = getBukkitPlayerSafe();
        if(op == null) op = Bukkit.getOfflinePlayer(name);

        return op;
    }

    public Player getBukkitPlayerSafe() {
        if (this.bukkitPlayer == null) {
            Player p = Bukkit.getPlayerExact(name);
            if (p != null) bukkitPlayer = p;
        }
        return bukkitPlayer;
    }

    public Player getBukkitPlayer() {
        return bukkitPlayer;
    }

    public void setBukkitPlayer(Player bukkitPlayer) {
        this.bukkitPlayer = bukkitPlayer;
    }

    public boolean isAfk() {
        return afk;
    }

    public void setAfk(boolean afk) {
        this.afk = afk;
        this.getBukkitPlayerSafe().setSleepingIgnored(afk);
    }

    public long getLastmessagetime() {
        return lastmessagetime;
    }

    public void setLastmessagetime(long lastmessagetime) {
        this.lastmessagetime = lastmessagetime;
    }

    public int getSpamcounter() {
        return spamcounter;
    }

    public void setSpamcounter(int spamcounter) {
        this.spamcounter = spamcounter;
    }

    public String getName() {
        return name;
    }

    public LocalPlayer(Player player) {
        this.bukkitPlayer = player;
        this.name = player.getName();
        this.uuid = player.getUniqueId();
    }

    public LocalPlayer(String name) {
        this.name = name;
    }

    public LocalPlayer(UUID uuid) {
        this.uuid = uuid;
    }

    public LocalPlayer(String name, UUID uuid) {
        this.name = name;
        this.uuid = uuid;
    }

    public ChatMode getChatMode() {
        return chatMode;
    }

    public void setChatMode(ChatMode chatMode) {
        this.chatMode = chatMode;
    }
    
    public void setCapeURL(String URL) {
        this.capeURL = URL;
    }

    public void setSkinURL(String URL) {
        this.skinURL = URL;
    }

    public boolean isInvisiblePickup() {
        return invisiblePickup;
    }

    public String getCapeURL() {
        return this.capeURL;
    }

    public String getSkinURL() {
        return this.skinURL;
    }

    /**
     * Try to resolve a ChatMode and set it
     *
     * @param chathandler The ChatHandler instance
     * @param chatmode    String defining the ChatMode
     * @throws ChatModeException Thrown when either the ChatMode isn't found, or isn't enabled
     */
    public boolean toggleChatMode(ChatHandler chathandler, String chatmode) throws ChatModeException {
        ChatMode cm = null;
        cm = chathandler.getChatMode(chatmode);

        if (cm != null && chathandler.getTCUtilsInstance().check(this.bukkitPlayer, "chat.mode." + chatmode)) {
            this.setChatMode(cm);
            return true;
        }

        return false;
    }
} public abstract class Permissions {

    public abstract String getUserPrefix(String player);

    public String getUserPrefix(String world, Player player)
        {return getUserPrefix(world, player.getName());}

    public abstract String getUserPrefix(String world, String player);

    public abstract String getPrimaryUserGroup(String world, String player);

    public String getUserSuffix(String world, Player player)
        {return getUserSuffix(world, player.getName());}

    public abstract String getUserSuffix(String world, String player);

    public abstract boolean inGroup(String group, Player player);

    public abstract boolean inGroup(String world, String group, Player player);

    public abstract boolean inSingleGroup(String world, String group, Player player);

    public abstract boolean hasPermission(String world, Player player, String permissionbit);


} public class PermissionsResolver {

    /* public static enum PermissionResolvingMode { BUKKIT, NIJIPERMS, PERMISSIONSEX }; */
    /* No longer needed! */

    public TweakcraftUtils plugin = null;
    private Permissions resolver = null;
    // private PermissionResolvingMode resolvmode = null;


    public PermissionsResolver(TweakcraftUtils instance) {
        this.plugin = instance;
    }

    public void init() {
        boolean foundPlugin = false;

        foundPlugin = tryPermissionsEx();
        if (!foundPlugin) foundPlugin = tryNijiPerms();
        if (!foundPlugin) tryZones();
        if (!foundPlugin) tryWEPIF();
        if (!foundPlugin) {
            plugin.getLogger().info("DinnerPerms found, using that for permissions resolving!");
            resolver = new BukkitPerms(this);
            plugin.getLogger().warning("Use this only as a fallback please, install and/or manage a real permissions plugin!");
        }
    }


    public Permissions getResolver() {
        if (resolver == null) init();
        return resolver;
    }

    public boolean hasPermission(String world, Player player, String permissionbit) {
        if (resolver == null) init();
        return resolver.hasPermission(world, player, permissionbit);
    }

    private void tryZones() {
        if(plugin.getConfigHandler().enableZones && plugin.getZones() != null) {
            resolver = new ZonesPerms(this, plugin.getZones());
            plugin.getLogger().info("Meaglin's Zones found, using that for permissions resolving!");
        }
    }

    private boolean tryPermissionsEx() {
        Plugin p = plugin.getServer().getPluginManager().getPlugin("PermissionsEx");
        if (p != null) {
            resolver = new PermsEx(this);
            plugin.getLogger().info("PermissionsEx found, using that for permissions resolving!");
            return true;
        }
        return false;
    }

    private boolean tryNijiPerms() {
        Plugin p = plugin.getServer().getPluginManager().getPlugin("Permissions");
        if (p != null) {
            resolver = new NijiPerms(this, ((com.nijikokun.bukkit.Permissions.Permissions) p).getHandler());
            plugin.getLogger().info("NijiPermissions found, using that for permissions resolving!");
            return true;
        }
        return false;
    }

    private boolean tryWEPIF() {
        if (plugin.getWorldEdit() != null) {
            resolver = new WEPIFPerms(this, plugin.getWorldEdit().getPermissionsResolver());
            plugin.getLogger().info("WorldEdit found, using that for permissions resolving!");
            plugin.getLogger().warning("Use this only as a fallback please, install and/or manage a real permissions plugin.!");
            return true;
        } else {
            return false;
        }
    }

} public class TamerTool {

    private TweakcraftUtils plugin;
    private Map<Player, TamerMode> tamers = new HashMap<Player, TamerMode>();

    public TamerTool(TweakcraftUtils instance) {
        this.plugin = instance;
    }

    public Map<Player, TamerMode> getTamers() {
        return tamers;
    }

    public void getOwner(Player player, Tameable tameable) {
        String className = tameable.getClass().getCanonicalName().split("Craft")[1];
        if (className.equals("Ocelot")) className = "Ozelot";
        if (className.equals("Horse")) className = "EntityHorse";
        EntityType entityType = EntityType.fromName(className);

        String cname = entityType.getName().toLowerCase();

        player.sendMessage(ChatColor.AQUA + "Tameable type : " + cname);

        if (tameable.isTamed()) {
            AnimalTamer tamer = tameable.getOwner();
            if (tamer instanceof Player || tamer instanceof OfflinePlayer) {
                // Player ptamer = (Player) tamer;
                boolean online = false;
                Player ptamer = null;
                OfflinePlayer offlinetamer = null;
                if (tamer instanceof Player) {
                    ptamer = (Player) tamer;
                    online = true;
                } else {
                    offlinetamer = (OfflinePlayer) tamer;
                    online = false;
                }

                boolean allowed = true;
                if (online && !ptamer.equals(player)) {
                    if (!plugin.check(player, "tamer.info.other"))
                        allowed = false;
                }
                if (allowed) {
                    player.sendMessage(ChatColor.AQUA + "Tameable owner : " + (online ? ptamer.getDisplayName() : offlinetamer.getName()));
                } else {
                    player.sendMessage(ChatColor.RED + "You do not have the permission to check the ownership of other wolves!");
                }
            } else {
                player.sendMessage(ChatColor.AQUA + "This tameable is owned by an entity/offline player!");
            }
        } else {
            player.sendMessage(ChatColor.AQUA + "This tameable isn't tamed!");
        }
        player.sendMessage(ChatColor.AQUA + "Tameable health : " + ((LivingEntity) tameable).getHealth());
        if (tameable instanceof Ageable) {
            player.sendMessage(ChatColor.AQUA + "Tameable age : " + ((Ageable) tameable).getAge() + (((Ageable) tameable).getAgeLock() ? ChatColor.RED + " L" : ""));
        }
        if (tameable instanceof Ocelot) {
            player.sendMessage(ChatColor.AQUA + "Tameable cat-type : " + ((Ocelot) tameable).getCatType().toString().toLowerCase());
        }
        if (tameable instanceof Horse) {
            Horse horse = (Horse) tameable;

            player.sendMessage(ChatColor.AQUA + "Tameable color : " + horse.getColor().toString().toLowerCase());
            player.sendMessage(ChatColor.AQUA + "Tameable style : " + horse.getStyle().toString().toLowerCase());
            player.sendMessage(ChatColor.AQUA + "Tameable variant : " + horse.getVariant().toString().toLowerCase());
            player.sendMessage(ChatColor.AQUA + "Tameable jumpstrength : " + horse.getJumpStrength());
            player.sendMessage(ChatColor.AQUA + "Tameable domestication : " + horse.getDomestication());
            player.sendMessage(ChatColor.AQUA + "Tameable maxdomestication : " + horse.getMaxDomestication());
        }
        if (tameable instanceof Wolf) {
            Wolf wolf = (Wolf) tameable;
            if (wolf.isAngry()) {
                player.sendMessage(ChatColor.AQUA + "This tameable is " + ChatColor.RED + "angry" + ChatColor.AQUA + "!");
                LivingEntity le = wolf.getTarget();
                String target = "";
                if (le instanceof OfflinePlayer)
                    if (((OfflinePlayer) le).isOnline())
                        target = ((Player) le).getDisplayName();
                    else
                        target = ((OfflinePlayer) le).getName();
                else {
                    String extraInfo = "";
                    if (le != null) {
                        EntityType et = le.getType();
                        extraInfo = et.getName();
                    }
                    target = "Animal " + extraInfo;// +le!=null?" ("+(et.getName())+")":"";
                }
                player.sendMessage(ChatColor.AQUA + "Target : " + target);
            }
        }
    }

    public void setTame(Tameable tameable, Boolean tame, Player player) {
        if (plugin.check(player, "tamer.tame")) {
            Boolean allowed = true;
            if (tame == null) {
                tame = !tameable.isTamed();
            }
            if (tameable.isTamed() && !tame) {
                AnimalTamer tamer = tameable.getOwner();

                if (tamer instanceof Player) {
                    if (!((Player) tamer).equals(player)) {
                        if (!plugin.check(player, "tamer.tame.untame.other"))
                            allowed = false;
                    } else {
                        if (!plugin.check(player, "tamer.tame.untame.own"))
                            allowed = false;
                    }
                }
                if (allowed) {
                    tameable.setOwner(null);
                    tameable.setTamed(false);
                } else {
                    player.sendMessage(ChatColor.RED + "You do not have permission to untame this tameables!");
                }
            } else if (!tameable.isTamed() && tame) {
                if (!plugin.check(player, "tamer.tame.tame"))
                    allowed = false;

                if (allowed) {
                    tameable.setTamed(true);
                    if (tameable instanceof Wolf) {
                        ((Wolf) tameable).setAngry(false);
                    }
                    ((Creature) tameable).setTarget(null);

                    tameable.setOwner(player);
                } else {
                    player.sendMessage(ChatColor.RED + "You do not have permission to tame this tameables!");
                }
            }
        } else {
            player.sendMessage(ChatColor.RED + "You do not have permission to tame tameables!");
        }
    }

    public void setAngry(Tameable tameable, Boolean angry, Player player) {
        if (plugin.check(player, "tamer.angry")) {
            if (tameable instanceof Wolf) {
                Wolf wolf = (Wolf) tameable;
                Boolean allowed = true;
                if (tameable.isTamed()) {
                    if (tameable.getOwner().equals(player)) {
                        if (!plugin.check(player, "tamer.angry.own"))
                            allowed = false;
                    } else {
                        if (!plugin.check(player, "tamer.angry.other"))
                            allowed = false;
                    }

                }
                if (angry == null) {
                    angry = !wolf.isAngry();
                }
                if (allowed) {
                    wolf.setAngry(angry);

                    if (!angry) {
                        wolf.setTarget(null);
                    }
                } else {
                    player.sendMessage(ChatColor.RED + "You do not have permission to anger this tameables!");
                }
            } else {
                player.sendMessage(ChatColor.RED + "You can't anger anything else than wolves!");
            }
        } else {
            player.sendMessage(ChatColor.RED + "You do not have permission to anger tameables!");
        }
    }

    public void sit(Tameable tameable, Boolean sit, Player player) {
        if (plugin.check(player, "tamer.sit")) {
            Boolean allowed = true;

            Boolean sitting = tameable instanceof Ocelot ? ((Ocelot) tameable).isSitting() :
                tameable instanceof Wolf && ((Wolf) tameable).isSitting();


            if (sitting) {
                if (tameable.getOwner() != null && tameable.getOwner().equals(player)) {
                    if (!plugin.check(player, "tamer.sit.own"))
                        allowed = false;
                } else {
                    if (!plugin.check(player, "tamer.sit.other"))
                        allowed = false;
                }
            } else {
                if (!plugin.check(player, "tamer.sit.wild"))
                    allowed = false;
            }
            if (sit == null)
                sit = !sitting;

            if (allowed) {
                if (tameable instanceof Wolf)
                    ((Wolf) tameable).setSitting(sit);
                if (tameable instanceof Ocelot)
                    ((Ocelot) tameable).setSitting(sit);
            } else
                player.sendMessage(ChatColor.RED + "You do not have permission to command this tameables!");
        } else {
            player.sendMessage(ChatColor.RED + "You do not have permission to command tameables!");
        }
    }

    public void heal(Tameable tameable, Boolean kill, Player player) {
        if (plugin.check(player, "tamer.heal")) {
            Boolean allowed = true;
            if (tameable.isTamed()) {
                if (tameable.getOwner() != null && tameable.getOwner().equals(player)) {
                    if (!plugin.check(player, "tamer.heal.own"))
                        allowed = false;
                } else {
                    if (!plugin.check(player, "tamer.heal.other"))
                        allowed = false;
                }
            } else {
                if (!plugin.check(player, "tamer.heal.wild"))
                    allowed = false;
            }

            if (allowed)
                ((LivingEntity) tameable).setHealth(((LivingEntity) tameable).getMaxHealth());
            else
                player.sendMessage(ChatColor.RED + "You do not have permission to heal this tameables!");
        } else {
            player.sendMessage(ChatColor.RED + "You do not have permission to heal tameables!");
        }
    }

    public void setAge(Tameable tameable, Integer age, Player player) {
        if (plugin.check(player, "tamer.setage")) {
            if (tameable instanceof Ageable) ((Ageable) tameable).setAge(age);
        }
    }

    public void setAgeLock(Tameable tameable, Boolean lock, Player player) {
        if (plugin.check(player, "tamer.setage")) {
            if (tameable instanceof Ageable) {
                player.sendMessage(ChatColor.BLUE + "Setting animal agelock to " + lock);
                ((Ageable) tameable).setAgeLock(lock);
            }
        }
    }

    public void handleEvent(Player player, Tameable tameable) {
        if (!tamers.containsKey(player))
            return;

        TamerMode tamermode = tamers.get(player);
        switch (tamermode.getMode()) {
            case INFO:
                this.getOwner(player, tameable);
                break;
            case TAME:
                this.setTame(tameable, tamermode.getState(), player);
                break;
            case ANGRY:
                this.setAngry(tameable, tamermode.getState(), player);
                break;
            case SIT:
                this.sit(tameable, tamermode.getState(), player);
                break;
            case HEAL:
                this.heal(tameable, tamermode.getState(), player);
                break;
            case SETAGELOCK:
                this.setAgeLock(tameable, tamermode.getState(), player);
                break;
            case SETAGE:
                this.setAge(tameable, tamermode.getData(), player);
                break;
        }
    }
} public class TeleportHistory {

    private TweakcraftUtils plugin;
    private Map<String, List<Location>> historymap = new HashMap<String, List<Location>>();
    private Map<String, Integer> historyoffset = new HashMap<String, Integer>();

    public TeleportHistory(TweakcraftUtils instance) {
        this.plugin = instance;
        this.historymap.clear();
        this.historyoffset.clear();
    }

    public int getRemaining(String playername) {
        if (!historymap.containsKey(playername)) {
            return 0;
        } else {
            return historymap.get(playername).size();
        }
    }

    public boolean atOrigin(String player) {
        if (!historymap.containsKey(player)) return true;
        int size = historymap.get(player).size() - 1;
        int position = 0;
        if (historyoffset.containsKey(player)) {
            Integer i = historyoffset.get(player);
            position += (i != null ? i : 0);
        }
        return position == size;
    }

    public List<Location> getHistoryList(String player) {
        if (!historymap.containsKey(player)) return null;
        return historymap.get(player);
    }

    // public int get

    public void setHistoryOffset(String player, Integer pos) {
        if ((pos == null || pos < 0)
            && historyoffset.containsKey(player)) {
            historyoffset.remove(player);
        } else {
            historyoffset.put(player, pos);
        }
    }

    public int getOffset(String player) {
        Integer offs = -1;
        if (historyoffset.containsKey(player)) {
            offs = historyoffset.get(player);
        }
        return offs != null ? offs : 0;
    }

    public Location get(String player, int position, Boolean back) {
        if (!plugin.getConfigHandler().enableTPBack) return null;
        if (!historymap.containsKey(player)) return null;
        List<Location> locList = historymap.get(player);

        if (position < 0 || position >= locList.size()) return null;

        if (back != null) {
            int offset = getOffset(player);
            offset = offset + (back ? 1 : -1);
            setHistoryOffset(player, offset);
        }

        return locList.get(position);
    }


    public void removeLast(String playername) {
        if (historymap.containsKey(playername)) {
            List<Location> locmap = historymap.get(playername);
            locmap.remove(locmap.size() - 1);
            if (locmap.size() == 0) {
                historymap.remove(playername);
            } else {
                historymap.put(playername, locmap);
            }
        }
    }

    public void removeNext(String playername) {
        if (historymap.containsKey(playername) && historyoffset.containsKey(playername)) {
            List<Location> locmap = historymap.get(playername);
            int pos = locmap.size() - getOffset(playername);
            locmap.remove(pos);
            if (locmap.size() == 0) {
                historymap.remove(playername);
            } else {
                historymap.put(playername, locmap);
            }
        }
    }

    public void addHistory(String playername, Location loc) {
        if (plugin.getConfigHandler().enableTPBack) {
            List<Location> locmap;
            if (loc.getY() > loc.getWorld().getMaxHeight() + 2 || loc.getY() < 1) { // failsafe
                loc.setY(loc.getWorld().getMaxHeight() + 2);
            }


            if (historymap.containsKey(playername)) {
                locmap = historymap.get(playername);

                if (historyoffset.containsKey(playername) && getOffset(playername) > 0) {

                    int siz = locmap.size();
                    int pos = siz - getOffset(playername) + 1;
                    /* List<Location> toRemove = */
                    locmap.subList(pos, siz).clear();
                    // locmap.en
                    // locmap.removeAll(toRemove);
                    setHistoryOffset(playername, null);
                }
                if (locmap.size() > 0 && !locmap.get(locmap.size() - 1).equals(loc)) locmap.add(loc);
            } else {
                locmap = new ArrayList<Location>();
                locmap.add(loc);
            }
            this.historymap.put(playername, locmap);
        }
    }

    public void clearHistory() {
        plugin.getLogger().info("Clearing complete TPBack history!");
        this.historymap.clear();
        this.historyoffset.clear();
    }

    public void clearFuture(String playername) {
        plugin.getLogger().info("Clearing TPBack future for player " + playername + "!");
        if (historymap.containsKey(playername) && historyoffset.containsKey(playername)) {
            List<Location> loclist = historymap.get(playername);
            int size = loclist.size();
            // loclist.removeAll();
            /* List<Location> toRemove = */
            loclist.subList(size - getOffset(playername), size).clear();
            /* loclist.removeAll(toRemove); */
            historymap.put(playername, loclist);
            setHistoryOffset(playername, null);
        }
    }

    public void clearHistory(String playername) {
        plugin.getLogger().info("Clearing TPBack history for player " + playername + "!");
        if (historymap.containsKey(playername)) {
            historymap.remove(playername);
        }
        if (historyoffset.containsKey(playername)) {
            historyoffset.remove(playername);
        }
    }
} public class UUIDResolver {

    private TweakcraftUtils plugin;
    private Map<String, UUID> uuids = new HashMap<String, UUID>();

    public UUIDResolver(TweakcraftUtils instance) {
        this.plugin = instance;
    }

    public UUID getOrCreateUUID(LocalPlayer lp) {

        if(uuids.containsKey(lp.getName()))
           return uuids.get(lp.getName());

        // List<PlayerOptions> popts = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", lp.getName()).findList();
        UUID retUUID = null;

        // if (popts.size() != 0) {
            PlayerOptions uuid = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", lp.getName()).ieq("optionname", "uuid").findUnique();
            if (uuid == null) {
                // Create one
                retUUID = lp.getBukkitOfflinePlayer().getUniqueId();
                if(retUUID == null) {
                    plugin.getLogger().warning("Didn't find any UUID for " + lp.getName() + ", is mojang's API down, or is this a fake player?!");
                    return null;
                }
                plugin.getLogger().warning("Didn't find a cached UUID for " + lp.getName() + ", updating PlayerOptions database!");
                uuid = new PlayerOptions();
                uuid.setName(lp.getName());
                uuid.setOptionname("uuid");
                uuid.setOptionvalue(retUUID.toString());
                plugin.getDatabase().save(uuid);
            } else {
                plugin.getLogger().log(Level.FINE, "We did find a cached UUID value for "+ lp.getName() + ", using that!");
                retUUID = UUID.fromString(uuid.getOptionvalue());
            }
        // }

        lp.setUuid(retUUID);
        uuids.put(lp.getName(), retUUID);
        return retUUID;
    }

    public UUID getOrCreateUUID(String player) {
        return getOrCreateUUID(plugin.wrapPlayer(player));
    }

    public void checkProfile(Player p) {

        if(uuids.containsKey(p.getName()))
            return;

        LocalPlayer lp = plugin.wrapPlayer(p);

        String player = p.getName();

        // Check if the person changed his name
        PlayerOptions oldName = plugin.getDatabase().find(PlayerOptions.class).where().ieq("optionname", "uuid").ieq("optionvalue", p.getUniqueId().toString()).findUnique();

        if (oldName != null && !oldName.getName().equalsIgnoreCase(player)) {
            plugin.getLogger().warning(oldName.getName() + " changed his name to " + player + ", updating PlayerOptions database!");

            List<PlayerOptions> oldOpts = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", oldName.getName()).findList();

            // Update DB!
            // plugin.getDatabase().find(PlayerOptions.class).where().ieq("name",  oldName).findList()
            for (Iterator<PlayerOptions> iterator = oldOpts.iterator(); iterator.hasNext(); ) {
                PlayerOptions next = iterator.next();
                next.setName(player);
            }
            plugin.getDatabase().save(oldOpts);

        }

        // List<PlayerOptions> popts = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", player).findList();
        // if(popts.size() != 0) {
            PlayerOptions uuid = plugin.getDatabase().find(PlayerOptions.class).where().ieq("name", player).ieq("optionname", "uuid").findUnique();
            if(uuid == null) {
                plugin.getLogger().warning(player + " had an old profile, updating PlayerOptions database!");
                uuid = new PlayerOptions();
                uuid.setName(player);
                uuid.setOptionname("uuid");
                uuid.setOptionvalue(p.getUniqueId().toString());
                plugin.getDatabase().save(uuid);
            }
        // }
    }
} public class WorldManager {

    private Map<String, iWorld> worlds;
    private TweakcraftUtils plugin;
    private int defaultViewDistance;
    private YamlConfiguration globalConfig = null;

    public Map<String, iWorld> getWorlds() {
        return worlds;
    }

    public World getDefaultWorld() {
        return plugin.getServer().getWorlds().get(0);
    }

    public Set<World> getDefaultWorlds() {
        boolean isNetherEnabled = plugin.getServer().getAllowNether();
        boolean isTheEndEnabled = plugin.getServer().getAllowEnd();

        int max = 0;
        if (isNetherEnabled) max++;
        if (isTheEndEnabled) max++;

        Set<World> worlds = new HashSet<World>();
        for (int i = 0; i <= max; i++)
            worlds.add(plugin.getServer().getWorlds().get(i));

        return worlds;
    }

    public int getDefaultViewDistance() {
        return plugin.getServer().getViewDistance();
    }

    public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
        // return null;
        /* if(worlds.containsKey(worldName)) {
            iWorld iw = worlds.get(worldName); */
        // I split on _'s, so it's flatgen_height_bottomlayer_proplayer_bedrocklayer
        String[] split = id.split("_");
        if (split[0].equalsIgnoreCase("flatgen")) {
            FlatGen fg = new FlatGen();
            if (split.length > 1) fg.setmapHeight(Integer.parseInt(split[1]));
            if (split.length > 2) fg.setNormal(Byte.parseByte(split[2]));
            if (split.length > 3) fg.setToplayer(Byte.parseByte(split[3]));
            if (split.length > 4) fg.setBedrockBottom(Boolean.parseBoolean(split[4]));
            plugin.getLogger().info("Utilising FlatGen for world " + worldName);
            return fg;
        } else if (split[0].equalsIgnoreCase("plotgen")) {
            PlotGen fg = new PlotGen();
            if (split.length > 1) fg.setmapHeight(Integer.parseInt(split[1]));
            if (split.length > 2) fg.setNormal(Byte.parseByte(split[2]));
            if (split.length > 3) fg.setToplayer(Byte.parseByte(split[3]));
            if (split.length > 4) fg.setBedrockBottom(Boolean.parseBoolean(split[4]));
            if (split.length > 5) fg.setPlotSize(Integer.parseInt(split[5]));
            plugin.getLogger().info("Utilising PlotGen for world " + worldName);
            plugin.getLogger().info("PlotGen PlotSize : " + fg.getPlotSize());
            // System.out.println("[");
            return fg;
        }
        // }
        /**
         * If all else fails, just provide a standard FlatGen
         */
        return new FlatGen();
    }

    public void loadMotd(String worldname) {
        if (!plugin.getConfigHandler().enableWorldMOTD || !worlds.containsKey(worldname)) return;

        try {
            File motd = new File(plugin.getDataFolder() + plugin.getConfigHandler().getDirSeperator() + "motd-" + worldname + ".txt");
            if (!motd.exists()) return;
            BufferedReader fr = new BufferedReader(new FileReader(motd));
            String line = null;
            List<String> lines = new ArrayList<String>();
            while ((line = fr.readLine()) != null) {
                lines.add(line.replace('&', '§'));
            }

            iWorld w = worlds.get(worldname);
            w.setMOTD(lines.toArray(new String[lines.size()]));
            plugin.getLogger().info("Loaded MOTD for world " + worldname + "!");
        } catch (FileNotFoundException e) {
            plugin.getLogger().warning("Couldn't find MOTD for world " + worldname + "!");
        } catch (IOException e) {
            plugin.getLogger().warning("Error while reading MOTD for world " + worldname + "!");
        }
    }

    public WorldManager(TweakcraftUtils instance) {
        this.plugin = instance;
        worlds = new HashMap<String, iWorld>();
    }

    public void setupWorlds() {

        if (globalConfig == null) globalConfig = plugin.getConfigHandler().getGlobalconfig();

        boolean netherWorldOnline = false;
        ConfigurationSection section = globalConfig.getConfigurationSection("worlds.extraworlds");
        if (section == null)
            return;

        Set<String> extraworlds = section.getKeys(false);

        Boolean worldInfDura = globalConfig.getBoolean("worlds.durability", true);

        if (plugin.getConfigHandler().enablemod_InfDura) {
            for (World w : getDefaultWorlds())
                w.setToolDurability(worldInfDura);

        }

        int defaultviewdistance = this.getDefaultViewDistance();

        for (org.bukkit.World world : plugin.getServer().getWorlds()) {
            // catch a /reload!
            // IWorld iw = new TweakWorld(this, world.getName(), world.getEnvironment(), world.getPVP(), world.getAllowMonsters(), world.getAllowMonsters(), defaultviewdistance, worldInfDura, true);
            if (extraworlds.contains(world.getName())) {
                TweakWorld tw = new TweakWorld(this, world.getName(), world.getEnvironment(), world.getPVP(), world.getAllowMonsters(), world.getAllowMonsters(), defaultviewdistance, worldInfDura, true);
                tw.setSpawnChunksActive(world.getKeepSpawnInMemory());
                worlds.put(world.getName(), tw);
            }
            if (world.getEnvironment() == Environment.NETHER)
                netherWorldOnline = true;
        }

        if (!netherWorldOnline && globalConfig.getBoolean("worlds.enablenether", false)) {
            String netherfolder = globalConfig.getString("worlds.netherfolder", "nether");
            if (!netherfolder.equalsIgnoreCase("")) {
                plugin.getLogger().info("Loading the netherworld!");
                worlds.put(netherfolder, new TweakWorld(this, netherfolder, Environment.NETHER, true));
            } else {
                plugin.getLogger().info("The nether's folder name can't be empty!");
            }
        }

        for (String node : extraworlds) {
            if (!worlds.containsKey(node)) {

                String env = globalConfig.getString("worlds.extraworlds." + node + ".environment", "");
                boolean enabled = globalConfig.getBoolean("worlds.extraworlds." + node + ".enabled", false);
                boolean pvp = globalConfig.getBoolean("worlds.extraworlds." + node + ".pvp", false);
                boolean monsters = globalConfig.getBoolean("worlds.extraworlds." + node + ".monsters", true);
                boolean animals = globalConfig.getBoolean("worlds.extraworlds." + node + ".animals", true);
                boolean durability = globalConfig.getBoolean("worlds.extraworlds." + node + ".durability", true);
                boolean addnether = globalConfig.getBoolean("worlds.extraworlds." + node + ".addnether", false);
                boolean addtheend = globalConfig.getBoolean("worlds.extraworlds." + node + ".addtheend", false);
                boolean spawnchunksactive = globalConfig.getBoolean("worlds.extraworlds." + node + ".spawnchunksactive", false);
                boolean allowFlight = globalConfig.getBoolean("worlds.extraworlds." + node + ".allowFlight", false);
                String chunkGenClass = globalConfig.getString("worlds.extraworlds." + node + ".chunkGeneratorClass", null);
                String chunkGen = globalConfig.getString("worlds.extraworlds." + node + ".chunkGenerator", null);
                String worldType = globalConfig.getString("worlds.extraworlds." + node + ".worldType", null);
                int viewdistance = globalConfig.getInt("worlds.extraworlds." + node + ".viewdistance", defaultviewdistance);
                int portalSearchRadius = globalConfig.getInt("worlds.extraworlds." + node + ".portalSearchRadius", 128);
                int difficulty = globalConfig.getInt("worlds.extraworlds." + node + ".difficulty", Difficulty.NORMAL.getValue());
                long seed = globalConfig.getLong("worlds.extraworlds." + node + ".seed", -1);
                long nseed = globalConfig.getLong("worlds.extraworlds." + node + ".netherseed", -1);

                String gameMode = globalConfig.getString("worlds.extraworlds." + node + ".gamemode", null);
                GameMode gm = gameMode != null ? GameMode.valueOf(gameMode.toUpperCase()) : null;

                /* if(gameMode==null || gameMode.equals("")) {
                    gm=GameMode.SURVIVAL;
                } */

                Environment wenv = null;
                if (env == null || env.equals("")) {
                    plugin.getLogger().info("World " + node + " does not have a valid environment definition, using \"normal\"");
                    wenv = Environment.NORMAL;
                } else if (env.equalsIgnoreCase("nether")) {
                    wenv = Environment.NETHER;
                } else if (env.equalsIgnoreCase("normal")) {
                    wenv = Environment.NORMAL;
                } else if (env.equalsIgnoreCase("end")) {
                    wenv = Environment.THE_END;
                } else {
                    wenv = Environment.valueOf(env.toUpperCase());
                }


                if (wenv == null) {
                    plugin.getLogger().info(env + " isn't a correct environment name!");
                } else {
                    plugin.getLogger().info("Adding world with name " + node + " and environmenttype " + env + "!");
                    plugin.getLogger().info("World " + node + " has pvp " + (pvp ? "enabled" : "disabled") + "!");
                    plugin.getLogger().info("World " + node + " monsters : " + (monsters ? "enabled" : "disabled") + ", animals : " + (animals ? "enabled" : "disabled") + "!");
                    if (addnether) plugin.getLogger().info("World " + node + " added nether world!");
                    if (addtheend) plugin.getLogger().info("World " + node + " added the_end world!");
                    plugin.getLogger().info("World " + node + " Tool Durability : " + (durability ? "enabled" : "disabled"));
                    plugin.getLogger().info("World " + node + " GameMode : " + (gm != null ? gm.toString().toLowerCase() : "Survival"));
                    plugin.getLogger().info("World " + node + " Difficulty : " + Difficulty.getByValue(difficulty).name().toLowerCase());
                    TweakWorld tw = new TweakWorld(this, node, wenv, pvp, monsters, animals, viewdistance, durability, false);
                    if (difficulty != getDefaultWorld().getDifficulty().getValue())
                        tw.setDifficulty(difficulty);
                    if (gm != null) tw.setGameMode(gm);
                    if (chunkGenClass != null) {
                        plugin.getLogger().info("World " + node + " is using a custom chunkGenClass using the deprecated method!");
                        plugin.getLogger().info("Consider using the newer method!");
                        tw.setChunkGenClass(chunkGenClass);
                    } else if (chunkGen != null) {
                        plugin.getLogger().info("World " + node + " is using a custom chunkGen!");
                        tw.setChunkGen(chunkGen);
                    }

                    if (worldType != null) {
                        WorldType type = WorldType.valueOf(worldType.toUpperCase());
                        if (type != null) {
                            plugin.getLogger().info("World " + node + " has worldType "+type.getName().toLowerCase()+"!");
                            tw.setWorldType(type);
                        }
                    }

                    tw.setEnabled(enabled);
                    if (enabled) {
                        if (seed != -1) tw.setSeed(seed, false);
                        tw.loadWorld();
                        tw.setSpawnChunksActive(spawnchunksactive);
                        tw.setAllowFlight(allowFlight);
                        if (addnether) {
                            if (nseed != -1) tw.setNetherseed(seed);
                            tw.addNether();
                            tw.setPortalSearchWidth(portalSearchRadius);
                        }
                        if(addtheend) {
                            if (nseed != -1) tw.setEndseed(seed);
                            tw.addTheEnd();
                        }
                    }

                    worlds.put(node, tw);
                    this.loadMotd(tw.getName());
                }
            } else {
                plugin.getLogger().info("World with name " + node + " already exists!");
            }
        }

    }

    public iWorld getWorld(String name) {
        return this.getWorld(name, false);
    }

    public iWorld getWorld(String name, boolean filterNether) {
        if (filterNether) {
            boolean isNether = name.endsWith("_nether");
            boolean isTheEnd = name.endsWith("_the_end");
            if (isNether) name = name.substring(0, name.length() - 7); // MINUS _nether
            if (isTheEnd) name = name.substring(0, name.length() - 8); // MINUS _the_end
        }
        if (worlds.containsKey(name)) {
            return worlds.get(name);
        } else {
            return null;
        }
    }

    public TweakcraftUtils getPlugin() {
        return plugin;
    }
}
import com.ensifera.animosity.craftirc.CraftIRC;
import com.ensifera.animosity.craftirc.EndPoint;
import com.guntherdw.bukkit.tweakcraft.Chat.ChatHandler;
import com.guntherdw.bukkit.tweakcraft.Chat.ChatMode;
import com.guntherdw.bukkit.tweakcraft.Commands.CommandHandler;
import com.guntherdw.bukkit.tweakcraft.Configuration.ConfigurationHandler;
import com.guntherdw.bukkit.tweakcraft.DataSources.Ban.BanHandler;
import com.guntherdw.bukkit.tweakcraft.DataSources.PersistenceClass.PlayerHistoryInfo;
import com.guntherdw.bukkit.tweakcraft.DataSources.PersistenceClass.PlayerInfo;
import com.guntherdw.bukkit.tweakcraft.DataSources.PersistenceClass.PlayerOptions;
import com.guntherdw.bukkit.tweakcraft.Events.TweakcraftUtilsEvent;
import com.guntherdw.bukkit.tweakcraft.Listeners.*;
import com.guntherdw.bukkit.tweakcraft.Packages.CraftIRCAdminEndPoint;
import com.guntherdw.bukkit.tweakcraft.Packages.CraftIRCEndPoint;
import com.guntherdw.bukkit.tweakcraft.Packages.ItemDB;
import com.guntherdw.bukkit.tweakcraft.Packages.LocalPlayer;
import com.guntherdw.bukkit.tweakcraft.Tools.Permissions.Permissions;
import com.guntherdw.bukkit.tweakcraft.Tools.PermissionsResolver;
import com.guntherdw.bukkit.tweakcraft.Tools.TamerTool;
import com.guntherdw.bukkit.tweakcraft.Util.TeleportHistory;
import com.guntherdw.bukkit.tweakcraft.Util.UUIDResolver;
import com.guntherdw.bukkit.tweakcraft.Worlds.WorldManager;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.zones.Zones;
import de.diddiz.LogBlock.LogBlock;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import javax.persistence.PersistenceException;
import java.io.*;
import java.util.*;
        }
    }

    public void setupWorldEdit() {
        Plugin plugin = this.getServer().getPluginManager().getPlugin("WorldEdit");

        if (we == null) {
            if (plugin != null) {
                we = (WorldEditPlugin) plugin;
            } /* We don't care if it failed because it's mostly a fallback for permissions. */
        }
    }

    public Zones getZones() {
        return zones;
    }

    public LogBlock getLogBlock() {
        return lb;
    }

    public void reloadMOTD() {
        File motdfile = new File(this.getDataFolder(), "motd.txt");
        MOTDLines = new ArrayList<String>();
        try {
            BufferedReader motdfilereader = new BufferedReader(new FileReader(motdfile));
            String line = "";
            while ((line = motdfilereader.readLine()) != null) {
                MOTDLines.add(line.replace('&', '§'));
            }
            motdfilereader.close();
        } catch (FileNotFoundException e) {
            this.getLogger().severe("MOTD file not found!");
        } catch (IOException e) {
            this.getLogger().severe("IOException occured while loadign the MOTD file!");
        }

    }

    public CommandHandler getCommandHandler() {
        return commandHandler;
    }

    public String getNickWithColors(String player) {
        String nick = this.wrapPlayer(player).getNick();

        String realname = player;
        if (nick == null) nick = realname;

        /// return getPlayerColor(realname, false) + nick + ChatColor.WHITE;
        return this.getPermissions().getResolver().getUserPrefix(player) + nick + ChatColor.WHITE;
    }

    public String getNick(String player) {
        String nick = playerListener.getNick(player);
        String realname = player;
        if (nick == null) nick = realname;
        return nick;
    }

    public boolean check(CommandSender sender, String permNode) {
        return !(sender instanceof Player) || this.check(((Player) sender), permNode);
    }

    public boolean check(Player player, String permNode) {
        return player.isOp() ||
            this.getPermissions().hasPermission(player.getWorld().getName(), player, "tweakcraftutils." + permNode);
    }

    public boolean checkfull(Player player, String permNode) {
        return player.isOp() ||
            this.getPermissions().getResolver().hasPermission(player.getWorld().getName(), player, permNode);
    }

    public BanHandler getBanhandler() {
        return banhandler;
    }

    public boolean hasNick(String player) {
        return playerListener.getNick(player) != null;
    }


    @Override
    public void onEnable() {
        pdfFile = this.getDescription();
        instance = this;

        // Check for "online mode", for UUID safety
        isServerInOnlineMode = this.getServer().getOnlineMode();

        donottplist = new HashSet<String>();
        MOTDLines = new ArrayList<String>();
        this.reloadMOTD();
        configHandler.setGlobalConfig(new File(this.getDataFolder(), "config.yml"));
        configHandler.reloadConfig();

        if(configHandler.enablePersistence) {
            if(!databaseloaded) {
                setupDatabase();
            }
        }

        this.cuiPlayers = new HashSet<Player>();
        this.mod_InfDuraplayers = new HashSet<Player>();

        this.setupWorldEdit();
        this.setupWorldGuard();
        this.setupCraftIRC();
        this.setupZones();
        this.setupLogBlock();
        // this.setupNoLagg();

        playerReplyDB = new HashMap<String, String>();
        this.registerEvents();

        itemDB.loadDataBase();
        banhandler.reloadBans();

        if (configHandler.enableIRC) {
            circendpoint = new CraftIRCEndPoint(this, "tcutils");