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 AdminChat extends ChatMode {

    private CraftIRC circ;
    private TweakcraftUtils plugin;

    public AdminChat(ChatHandler instance) {
        super(instance);
        this.plugin = instance.getTCUtilsInstance();
        if (plugin.getConfigHandler().enableIRC) {
            this.circ = plugin.getCraftIRC();
            circ.registerEndPoint("tcutilsadmin", plugin.getAdminEndPoint());
        }
    }

    @Override
    public boolean sendMessage(CommandSender sender, String message) {

        String sendername = "";
        String pcolor = "";
        String cleanname;
        if (sender instanceof Player) {
            sendername = ((Player) sender).getDisplayName(); // ((Player)sender).getName();
            pcolor = ""; // getDisplayName handles this one!
            cleanname = ((Player) sender).getName();
        } else {
            sendername = "CONSOLE";
            cleanname = sendername;
            pcolor = ChatColor.LIGHT_PURPLE.toString();
        }
        String msg = ChatColor.GREEN + "A: [" + pcolor + sendername + ChatColor.GREEN + "] " + message;
        if (plugin.getConfigHandler().enableIRC && plugin.getCraftIRC() != null && plugin.getConfigHandler().AIRCenabled) {

            String targetmsg = plugin.getConfigHandler().AIRCMessageFormat;
            targetmsg = targetmsg.replace("%name%", cleanname).
                replace("%message%", message).
                replace("%dispname%", ChatColor.stripColor(sendername));

            targetmsg = targetmsg.replace("%clearcolors%", Character.toString((char) 3));
            RelayedMessage rmsg = plugin.getCraftIRC().newMsgToTag(plugin.getAdminEndPoint(), plugin.getConfigHandler().AIRCtag, "chat");

            rmsg.setField("sender", pcolor + sendername);
            rmsg.setField("realSender", cleanname);
            rmsg.setField("message", targetmsg);
            if (sender instanceof Player) {
                Player p = (Player) sender;
                // World w = p.getWorld();
                rmsg.setField("world", p.getWorld().getName());
                rmsg.setField("prefix", plugin.getPermissions().getResolver().getUserPrefix(p.getWorld().getName(), p));
                rmsg.setField("suffix", plugin.getPermissions().getResolver().getUserSuffix(p.getWorld().getName(), p));
            }
            rmsg.post();
        }

        if (sender instanceof Player && !isOnList(sender)) {
            sender.sendMessage(msg);
        }
        for (Player p : getRecipients(sender)) {
            p.sendMessage(msg);
        }
        plugin.getLogger().info("AMSG: <" + sendername + "> " + message);
        return true;
    }

    @Override
    public boolean broadcastMessage(CommandSender sender, String message) {

        String msg = message;
        if (sender instanceof Player && !isOnList(sender)) {
            sender.sendMessage(msg);
        }
        for (Player p : getRecipients(sender)) {
            p.sendMessage(msg);
        }
        plugin.getLogger().info("AMSG: " + message);
        return true;
    }

    @Override
    public boolean broadcastMessage(String message) {
        // String msg = message;
        for (Player p : getRecipients(null)) {
            p.sendMessage(message);
        }
        plugin.getLogger().info("AMSG: " + message);
        return true;
    }

    public boolean broadcastMessageRealAdmins(String message) {
        for (Player player : getAdmins()) {
            player.sendMessage(message);
        }
        plugin.getLogger().info("AMSG: " + message);
        return true;
    }

    @Override
    public Set<Player> getRecipients(CommandSender sender) {
        Set<Player> recp = new HashSet<Player>();
        for (String m : subscribers) {
            Player p = plugin.getServer().getPlayer(m);
            if (p != null)
                recp.add(p);
        }

        for (Player p : plugin.getServer().getOnlinePlayers()) {
            if (plugin.check(p, "admon")) {
                if (!recp.contains(p))
                    recp.add(p);
            }
        }
        return recp;
    }

    public boolean isOnList(CommandSender sender) {
        if (sender instanceof Player) {
            return getRecipients(sender).contains((Player) sender);
        } else {
            return true;
        }
    }

    public List<Player> getAdmins() {
        List<Player> recp = new ArrayList<Player>();
        for (Player p : plugin.getServer().getOnlinePlayers()) {
            if (plugin.check(p, "admon"))
                recp.add(p);
        }
        return recp;
    }

    public List<String> getAdminsString() {
        List<String> admins = new ArrayList<String>();
        for (Player m : getAdmins()) {
            admins.add(m.getName());
        }
        return admins;
    }

    @Override
    public String getDescription() {
        return "Admin chat (needs permissions!)";
    }

    public boolean sendToRealAdmins(CommandSender sender, String message) {
        for (Player player : getAdmins()) {
            player.sendMessage(message);
        }
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }

    public boolean isPlayerAllowed(String playername) {
        return subscribers.contains(playername);
    }

    @Override
    public String getColor() {
        return ChatColor.GREEN.toString();
    }

    @Override
    public String getPrefix() {
        return this.getColor() + "A" + ChatColor.WHITE.toString();
    }
} 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!");
    }
} 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 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 final class ArgumentParser {

    private Map<String, Argument> _namedargs;
    private List<Argument> _args;
    private List<String> _flagsUsed;

    public ArgumentParser(String[] args) {
        _namedargs = new HashMap<String, Argument>();
        _args = new ArrayList<Argument>();
        _flagsUsed = new ArrayList<String>();
        int x=0;
        for(String arg: args) {
            if(arg.startsWith("-")) { /* Flag type */
                Argument argument = new Argument(x, arg.substring(1), null);
                argument.setArgType(true);
                if(args.length > x+1) { /* Add the value as well! */
                    argument.setArgvalue(args[x+1]);
                }
                _namedargs.put(argument.getArgname(), argument);
                _args.add(argument);
            } else { /* Possible argname:argvalue type */
                String[] argslist = arg.split("\\:", 2);
                // _args.put(argslist)
                if(argslist.length>1) {
                    argslist[0] = argslist[0].toLowerCase();
                    Argument argument = new Argument(x, argslist[0], argslist[1]);
                    argument.setArgType(false);
                    _namedargs.put(argslist[0], argument);
                    _args.add(argument);
                } else {
                    Argument argument = new Argument(x, null, argslist[0]);
                    _args.add(argument);
                }
            }
            // arglist
            x++;
        }
    }

    public ArgumentParser() { }

    public void setUsed(String argName, boolean state) {
        Argument arg = null;
        // First the named argument map, this is the easiest one.
        arg = _namedargs.get(argName);
        if(arg!=null)  {
            arg.set_used(state);
            if(arg.isArgType()) {
                int pos = arg.getId();
                if(_args.size()>pos+1) _args.get(pos+1).set_used(true);
            }
        }
        // Second the List<Argument> one, this'll require a for loop
        for(int x=0; x<_args.size(); x++) {
            arg = _args.get(x);
            if(arg.getArgname() != null && arg.getArgname().equals(argName)) arg.set_used(state);
            if(arg.isArgType()) {
                if(_args.size()>x+1) _args.get(x+1).set_used(true);
            }
        }
    }

    public int getSize() {
        return _args.size();
    }

    public Object getValue(int pos) {
        if(pos<0)
            return null;
        else if(pos>this.getSize())
            return null;
        else {
            this.setUsed(_args.get(pos).getArgname(), true);
            return _args.get(pos).getArgvalue();
        }
    }

    public Boolean getBoolean(String argname, Boolean defaultval) {
        if(_namedargs.containsKey(argname)) {
            this.setUsed(argname, true);
            return Boolean.parseBoolean((String) _namedargs.get(argname).getArgvalue());
        } else
            return defaultval;
    }

    public Integer getInteger(String argname, Integer defaultval) {
        if(_namedargs.containsKey(argname)) {
            this.setUsed(argname, true);
            return Integer.parseInt((String) _namedargs.get(argname).getArgvalue());
        } else
            return defaultval;
    }

    public Boolean getBoolean(int pos, Boolean defaultval) {
        if(pos >=0 && this.getSize()>pos) {
            this.setUsed(_args.get(pos).getArgname(), true);
            return Boolean.parseBoolean((String) _args.get(pos).getArgvalue());
        } else
            return defaultval;
    }

    public Integer getInteger(int pos, Integer defaultval) {
        if(pos >=0 && this.getSize()>pos) {
            this.setUsed(_args.get(pos).getArgname(), true);
            return Integer.parseInt((String)_args.get(pos).getArgvalue());
        } else
            return defaultval;
    }

    public String getString(int pos, String defaultval) {
        if(pos >=0 && this.getSize()>pos) {
            this.setUsed(_args.get(pos).getArgname(), true);
            return (String)_args.get(pos).getArgvalue();
        } else
            return defaultval;
    }

    public String getString(String argname, String defaultval) {
        if(_namedargs.containsKey(argname)) {
            this.setUsed(argname, true);
            return (String) _namedargs.get(argname).getArgvalue();
        }
        else
            return defaultval;
    }

    public boolean is_Flag(Argument arg) {
        boolean flag = false;
        int pos = arg.getId();
        if(pos < _args.size()) {
            if(arg.isArgType()) flag = true;
        }
        if(!flag && pos > 0) {
            if(_args.get(pos-1).isArgType()) flag = true;
        }
        return flag;
    }

    public List<Argument> getDefaultArguments() {
        List<Argument> vals = new ArrayList<Argument>();
        for(Argument arg: _args) {
            if(!is_Flag(arg) && arg.getArgname() == null)
                vals.add(arg);
        }
        return vals;
    }

    public List<Argument> getUnusedArgsList() {
        List<Argument> vals = new ArrayList<Argument>();
        for(Argument arg : _args) {
            if(is_Flag(arg) && !arg.is_used()) {
                vals.add(arg);
            } else if(arg.getArgname()==null || !arg.is_used())
                vals.add(arg);
        }
        return vals;
    }

    public boolean isflagUsed(String needle) { /* Reserved for simple flag checking */
        for(Argument arg : _args) {
            String lookFor = null;
            if(arg.isArgType())
                if(arg.getArgname().equals(needle)) {
                    _flagsUsed.add(needle);
                    return true;
                }
        }
        return false;
    }

    public String[] getUnusedArgs() {
        // return (String[]) this.getUnusedArgsList().toArray();
        List<Argument> als = this.getUnusedArgsList();
        // String[] args = new String[als.size()];
        List<String> args = new ArrayList<String>();
        int x=0;
        while(x<als.size()) {
            String tmp = "";
            Argument arg = als.get(x);
            if(is_Flag(arg)) {
                boolean flagUsed = false;
                if(arg.getArgname()!=null) {
                    flagUsed =  _flagsUsed.contains(arg.getArgname());
                    if(!flagUsed) { args.add("-"+arg.getArgname()); }
                }
                if(arg.getArgvalue()!=null) { x++; tmp+=(String)arg.getArgvalue(); args.add(tmp); }
            } else {
                if(arg.getArgname()!=null) { tmp+=arg.getArgname()+":"; }
                if(arg.getArgvalue()!=null) tmp+=(String)arg.getArgvalue();
                args.add(tmp);
            }
            x++;
        }
        return args.toArray(new String[args.size()]);
    }

    public String[] getNormalArgs() {
        List<Argument> al = this.getDefaultArguments();
        String[] lijst = new String[al.size()];
        for(int x=0;x<al.size();x++) {
            lijst[x]=(String)al.get(x).getArgvalue();
        }
        return lijst;
    }

} public class TweakcraftUtils extends JavaPlugin {

    protected WorldGuardPlugin wg = null;
    protected CraftIRC circ = null;
    protected Zones zones = null;
    protected LogBlock lb = null;
    protected WorldEditPlugin we = null;
    // protected TCUtilsClientBridgePlugin tcUtilsClientBridgePlugin = null;
    // protected NoLagg nolagg = null;
    // protected PermissionHandler ph = null;

    private final TweakcraftPlayerListener playerListener = new TweakcraftPlayerListener(this);
    private final TweakcraftEntityListener entityListener = new TweakcraftEntityListener(this);
    private final TweakcraftBlockListener blockListener = new TweakcraftBlockListener(this);
    private final TweakcraftWorldListener worldListener = new TweakcraftWorldListener(this);
    private final CommandHandler commandHandler = new CommandHandler(this);
    private final BanHandler banhandler = new BanHandler(this);
    private final ItemDB itemDB = new ItemDB(this);
    private final WorldManager worldmanager = new WorldManager(this);
    private final ConfigurationHandler configHandler = new ConfigurationHandler(this);
    private final TeleportHistory telehistory = new TeleportHistory(this);
    private final TamerTool tamertool = new TamerTool(this);
    private final ChatHandler chathandler = new ChatHandler(this);
    private final PermissionsResolver permsResolver = new PermissionsResolver(this);

    private final UUIDResolver uuidResolver = new UUIDResolver(this);

    private HashMap<String, LocalPlayer> localPlayers = new HashMap<String, LocalPlayer>();

    private Object circendpoint = null;
    private Object cirdlogendpoint = null;
    private Object circadminendpoint = null;

    private Set<String> donottplist;
    private List<String> MOTDLines;

    protected PluginDescriptionFile pdfFile = null;

    private Set<Player> cuiPlayers;
    public String CUIPattern = "§7§3§3§4";

    private Set<Player> mod_InfDuraplayers;
    public String ToolDuraPattern = "§1§1§1§1";

    public static TweakcraftUtils instance = null;

    public Map<String, String> playerReplyDB;
    public boolean databaseloaded = false;

    public boolean isServerInOnlineMode = false;


    public Set<Player> getMod_InfDuraplayers() {
        return mod_InfDuraplayers;
    }

    public PermissionsResolver getPermissions() {
        return this.permsResolver;
    }

    public Permissions getPermissionsResolver() {
        return this.getPermissions().getResolver();
    }

    public Set<Player> getCUIPlayers() {
        return cuiPlayers;
    }

    public static TweakcraftUtils getInstance() {
        /* Do not create a new instance, could be dangerous!
         * It'll get set at the onEnable() method!
         */
        /* if(instance==null)
            instance = new TweakcraftUtils(); */

        return instance;
    }

    public void sendCUIChatMode(Player player) {
        if (this.cuiPlayers != null && this.cuiPlayers.contains(player)) {
            ChatMode cm = getChathandler().getPlayerChatMode(player);

            if (cm == null)
                player.sendRawMessage(CUIPattern + "null");
            else
                player.sendRawMessage(CUIPattern + "[" + cm.getPrefix() + "]");
        }
        /* if(tcUtilsClientBridgePlugin != null)
            tcUtilsClientBridgePlugin.sendChatMode(player); */
        TweakcraftUtilsEvent event = new TweakcraftUtilsEvent(TweakcraftUtilsEvent.Action.CHATMODE_CHANGED);
        event.setPlayer(wrapPlayer(player));
        this.getServer().getPluginManager().callEvent(event);
    }

    public LocalPlayer wrapPlayer(Player player) {
        LocalPlayer p = this.wrapPlayer(player.getName());
        if (p.getBukkitPlayer() == null || p.getBukkitPlayer() != player)
            p.setBukkitPlayer(player);

        if(isServerInOnlineMode && p.getUuid() == null)
            p.setUuid(player.getUniqueId());

        return p;
    }

    public LocalPlayer wrapPlayer(String player) {
        if (!this.localPlayers.containsKey(player.toLowerCase()))
            this.localPlayers.put(player.toLowerCase(), new LocalPlayer(player));

        return this.localPlayers.get(player.toLowerCase());
    }

    public void sendCUIHandShake(Player player) {
        if (getConfigHandler().enableCUI) {
            player.sendRawMessage(CUIPattern); // HANDSHAKE
        }
    }

    public UUIDResolver getUUIDResolver() {
        return uuidResolver;
    }

    /**
     * Send the player a mod_InfDura string, setting the new ToolDurabilty mode
     *
     * @param player The player to send the mode to
     */
    public void sendToolDuraMode(Player player) {
        this.sendToolDuraMode(player, player.getWorld());
    }

    /**
     * Send the player a mod_InfDura string, setting the new ToolDurabilty mode
     *
     * @param player The player to send the mode to
     * @param world  The world to check the ToolDurability mode
     */
    public void sendToolDuraMode(Player player, World world) {
        if (getConfigHandler().enablemod_InfDura && mod_InfDuraplayers.contains(player)) {
            player.sendRawMessage(ToolDuraPattern + world.getToolDurability());
        }
        /* if(tcUtilsClientBridgePlugin != null)
            tcUtilsClientBridgePlugin.getPlayerListener().sendToolDuraMode(player, world); */

    }

    /**
     * Send the specified player the mod_InfDura handshake
     *
     * @param player The player that will receive the initiation
     */
    public void sendmod_InfDuraHandshake(Player player) {
        if (getConfigHandler().enablemod_InfDura) {
            player.sendRawMessage(ToolDuraPattern);
        }
    }

    public String findinlist(String find, Set<String> list) {
        for (String name : list) {
            if (name.toLowerCase().contains(find.toLowerCase())) {
                return name;
            }
        }
        return null;
    }

    /**
     * Get the Teleport History tool
     *
     * @return TeleportHistory instance
     */
    public TeleportHistory getTelehistory() {
        return telehistory;
    }

    /**
     * Get the PlayerListener instance
     *
     * @return TweakcraftUtils's PlayerListener instance
     */
    public TweakcraftPlayerListener getPlayerListener() {
        return playerListener;
    }

    /**
     * Get the ItemDB instance
     *
     * @return ItemDB instance
     */
    public ItemDB getItemDB() {
        return itemDB;
    }

    /**
     * Get the WorldManager instance
     *
     * @return WorldManager instance
     */
    public WorldManager getworldManager() {
        return worldmanager;
    }

    /**
     * Get the TamerTool tool instance
     *
     * @return TamerTool instance
     */
    public TamerTool getTamerTool() {
        return tamertool;
    }

    public String getCompassDirection(Float rotation) {
        String dir = null;
        Float rot = (rotation - 90) % 360;
        if (rot < 0) {
            rot += 360;
        }
        Integer r = rot.intValue();

        if (r < 23)
            dir = "N";
        else if (r < 68)
            dir = "NE";
        else if (r < 113)
            dir = "E";
        else if (r < 158)
            dir = "SE";
        else if (r < 203)
            dir = "S";
        else if (r < 248)
            dir = "SW";
        else if (r < 293)
            dir = "W";
        else if (r < 338)
            dir = "NW";
        else
            dir = "N";

        return dir;
    }

    @Override
    public List<Class<?>> getDatabaseClasses() {
        List<Class<?>> list = new ArrayList<Class<?>>();
        list.add(PlayerInfo.class);
        list.add(PlayerHistoryInfo.class);
        list.add(PlayerOptions.class);
        return list;
    }

    public void setupDatabase() {
        try {
            getDatabase().find(PlayerInfo.class).findRowCount();
            getDatabase().find(PlayerOptions.class).findRowCount();
            if (configHandler.useTweakBotSeen)
                getDatabase().find(PlayerHistoryInfo.class).findRowCount();
        } catch (PersistenceException ex) {
            this.getLogger().info("Installing database for " + getDescription().getName() + " due to first time usage");
            if (configHandler.useTweakBotSeen)
                this.getLogger().info("Also creating the TweakBot !seen helpen table");
            installDDL();
        }
        databaseloaded = true;
    }

    public String listToString(List<String> lijst) {
        StringBuilder builder = new StringBuilder();
        int c = 0;
        if (lijst.size() != 0) {
            while (c < lijst.size()) {
                builder.append(lijst.get(c));
                if (c != lijst.size() - 1) builder.append(",");
                c++;
            }
        }
        return builder.toString();
    }

    private List<String> toList(String str) {
        List<String> result = new ArrayList<String>();
        try {
            String[] names = str.split(",");
            for (String n : names)
                result.add(n.trim());

        } catch (NullPointerException e) {
            // result = new ArrayList<String>();
        }
        return result;
    }

    public static List<Player> findPlayerByNick(String part) {
        /* return  getPlayerListener().findPlayerNameByNick(part); */
        if (getInstance() != null)
            return getInstance().findPlayerasPlayerList(part);
        else
            return null;
    }

    public static List<String> findPlayerByNick_string(String part) {
        if (getInstance() != null) {
            List<Player> lijst = getInstance().findPlayerasPlayerList(part);
            List<String> userList = new ArrayList<String>();
            if(lijst.size()>0) {
                for(Player p : lijst) {
                    userList.add(p.getName());
                }
            }
            return userList;
        } else
            return null;
    }

    public Player findPlayerasPlayer(String partOfName) {
        // Go for the nicks first!
        Player nick = playerListener.findPlayerByNick(partOfName);
        if (nick == null) {
            for (Player p : this.getServer().getOnlinePlayers()) {
                if (p.getName().toLowerCase().contains(partOfName.toLowerCase())) // found, return the fullname!
                    return p;
            }
        }

        // not found, just return partOfName
        return nick;
    }

    public Player getPlayer(String nick, boolean findNick) {
        Player p = findNick ? getPlayerByNick(nick) : null;
        if (p == null) p = this.getServer().getPlayerExact(nick);
        return p;
    }

    public Player getPlayerByNick(String nick) {
        return playerListener.findPlayerByNick(nick, true);
    }

    public List<Player> findPlayerasPlayerList(String partOfName) {
        // Go for the nicks first!
        List<Player> players = playerListener.findPlayersByNick(partOfName);
        for (Player p : this.getServer().getOnlinePlayers()) {
            if (p.getName().toLowerCase().contains(partOfName.toLowerCase())
                && !players.contains(p)) // found, return the fullname!
                players.add(p);
        }
        return players;
    }


    public String findPlayer(String partOfName) {
        // Go for the nicks first!
        for (Player p : this.getServer().getOnlinePlayers()) {
            if (p.getName().toLowerCase().contains(partOfName.toLowerCase())) // found, return the fullname!
                return p.getName();
        }
        // not found, just return partOfName
        return partOfName;
    }


    /**
     * Register the Bukkit events to the appropriate event handlers.
     */
    private void registerEvents() {
        PluginManager manager = getServer().getPluginManager();

        manager.registerEvents(playerListener, this);
        manager.registerEvents(entityListener, this);
        manager.registerEvents(blockListener, this);
        manager.registerEvents(worldListener, this);
    }

    public TweakcraftWorldListener getWorldListener() {
        return worldListener;
    }

    public List<String> getMOTD() {
        return MOTDLines;
    }

    public CraftIRC getCraftIRC() {
        return circ;
    }

    public WorldEditPlugin getWorldEdit() {
        return we;
    }

    public WorldGuardPlugin getWorldGuard() {
        return wg;
    }

    public String getPlayerColor(String playername) {
        return this.getPlayerColor(playername, false);
    }

    public String getPlayerColor(String playername, boolean change) {
        String pref = "";
        Player p = null;
        try {
            p = this.getServer().getPlayerExact(playername);
            pref = this.getPermissions().getResolver().getUserPrefix(p.getWorld().getName(), p);
        } catch (NullPointerException e) {
            pref = "§f";
        }
        String col = ChatColor.WHITE.toString();
        if (p == null && change) col = ChatColor.AQUA + "[NC] " + pref;
        if (p != null) col += pref;
        return col;

    }

    public ChatHandler getChathandler() {
        return this.chathandler;
    }

    @Deprecated
    public static List<String> splitUp(String msg) {
        int maxlength = 55;
        List<String> lijst = new ArrayList<String>();
        String toadd;
        int x = 0;
        while (x < msg.length() - maxlength) {
            toadd = msg.substring(x, x + maxlength);
            if (!toadd.trim().equals(""))
                lijst.add(toadd.trim());
            x += maxlength;
        }
        lijst.add(msg.substring(x));

        return lijst;
    }


    public void setupCraftIRC() {
        if (this.getConfigHandler().enableIRC) {
            Plugin plugin = this.getServer().getPluginManager().getPlugin("CraftIRC");

            if (circ == null) {
                if (plugin != null) {
                    circ = (CraftIRC) plugin;
                } else {
                    this.getConfigHandler().enableIRC = false;
                    this.getLogger().warning("WARNING: Couldn't find CraftIRC, but is enabled in the config.");
                    this.getLogger().warning("WARNING: Disabling CraftIRC support.");
                }
            }
        }
    }

    public void setupWorldGuard() {
        if (this.getConfigHandler().enableWorldGuard) {
            Plugin plugin = this.getServer().getPluginManager().getPlugin("WorldGuard");

            if (wg == null) {
                if (plugin != null) {
                    wg = (WorldGuardPlugin) plugin;
                } else {
                    this.getConfigHandler().enableWorldGuard = false;
                    this.getLogger().warning("WARNING: Couldn't find WorldGuard, but is enabled in the config.");
                    this.getLogger().warning("WARNING: Disabling WorldGuard support.");
                }
            }
        }
    }

    public void setupZones() {
        if (this.getConfigHandler().enableZones) {
            Plugin plugin = this.getServer().getPluginManager().getPlugin("Zones");

            if (zones == null) {
                if (plugin != null) {
                    zones = (Zones) plugin;
                } else {
                    this.getConfigHandler().enableZones = false;
                    this.getLogger().warning("WARNING: Couldn't find Zones, but is enabled in the config.");
                    this.getLogger().warning("WARNING: Disabling Zones support.");
                }
            }
        }
    }

    public void setupLogBlock() {
        if (this.getConfigHandler().enableLogBlock) {
            Plugin plugin = this.getServer().getPluginManager().getPlugin("LogBlock");

            if (lb == null) {
                if (plugin != null) {
                    lb = (LogBlock) plugin;
                } else {
                    this.getConfigHandler().enableLogBlock = false;
                    this.getLogger().warning("WARNING: Couldn't find LogBlock, but is enabled in the config.");
                    this.getLogger().warning("WARNING: Disabling LogBlock support.");
                }
            }
        }
    }

    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");
            cirdlogendpoint = new CraftIRCEndPoint(this, "tcutilslog");
            circadminendpoint = new CraftIRCAdminEndPoint(this);
        }

        if (configHandler.enableDebug)
            commandHandler.checkCommands();

        commandHandler.addHelpTopics();

        /* itemDB.writeDB(); */

        playerListener.reloadInvisTable();
        worldmanager.setupWorlds();

        this.getLogger().info("[" + pdfFile.getName() + "] " + pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!");

    }

    public EndPoint getEndPoint() {
        return (EndPoint) circendpoint;
    }

    public EndPoint getAdminEndPoint() {
        return (EndPoint) circadminendpoint;
    }
    
    public String getVersion() {
        return pdfFile.getVersion();
    }
    
    public EndPoint getLogginEndPoint() {
        return (EndPoint) cirdlogendpoint;
    }

    public Set<String> getDonottplist() {
        return donottplist;
    }

    @Override
    public void onDisable() {
        instance = null;
        this.getLogger().info("[" + pdfFile.getName() + "] Shutting down!");
        // this.getDatabase().
    }

    public ConfigurationHandler getConfigHandler() {
        return configHandler;
    }

    @Override
    public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
        return worldmanager.getDefaultWorldGenerator(worldName, id);
    }

    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] unfilteredargs) {

        List<String> argsa = new ArrayList<String>();
        // String[] args = new String[];
        //int argc = 0;
        for (String a : unfilteredargs) {
            if (a != null && !a.trim().equals("")) {
                argsa.add(a);
                // argc++;
            }
        }
        String[] args = argsa.toArray(new String[argsa.size()]);

        if (commandHandler.getCommandMap().containsKey(cmd.getName())) {
            return commandHandler.executeCommand(sender, cmd.getName(), unfilteredargs);
        }
        return false;
    }
} public class TweakWorld implements iWorld {

    private org.bukkit.World world;
    private org.bukkit.World nether;
    private org.bukkit.World the_end;

    private String foldername = null;
    private boolean enabled = false;
    private boolean allowmonsters = true;
    private boolean allowanimals = true;
    private boolean tooldurability = true;
    private boolean nether_enabled = false;
    private boolean the_end_enabled = false;
    private boolean keepspawnactive = false;
    private int difficulty = Difficulty.NORMAL.getValue();
    private Long seed = null;
    private Long netherseed = null;
    private Long endseed = null;
    private int portalSearchWidth = 128;
    private String worldName = "";
    private String chunkGenClass = null;
    private ChunkGenerator chunkgen = null;
    private org.bukkit.World.Environment environment;
    private org.bukkit.WorldType worldType = null;
    private WorldManager wm;
    private boolean pvp = true;
    private int viewdistance = 10;
    private GameMode gamemode = null;
    private boolean allowFlight = false;
    private String[] motd;

    public TweakWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean enabled) {
        this.wm = wm;
        this.foldername = foldername;
        this.environment = env;
        this.allowanimals = true;
        this.allowmonsters = true;
        this.tooldurability = true;
        this.enabled = enabled;
        if (enabled) {
            loadWorld(wm, foldername, env, enabled);
        }
    }

    public TweakWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean pvp, boolean allowMonsters, boolean allowAnimals, boolean enabled) {
        this.wm = wm;
        this.foldername = foldername;
        this.environment = env;
        this.pvp = pvp;
        this.allowanimals = allowAnimals;
        this.allowmonsters = allowMonsters;
        this.tooldurability = true;
        this.enabled = enabled;
        if (enabled) {
            loadWorld(wm, foldername, env, enabled);
        }
    }

    public TweakWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean pvp, boolean allowMonsters, boolean allowAnimals, int viewdistance, boolean enabled) {
        this.wm = wm;
        this.foldername = foldername;
        this.environment = env;
        this.pvp = pvp;
        this.allowanimals = allowAnimals;
        this.allowmonsters = allowMonsters;
        this.enabled = enabled;
        this.tooldurability = true;
        if (viewdistance > 3 && viewdistance < 16)
            this.viewdistance = viewdistance;
        else
            this.viewdistance = wm.getDefaultViewDistance();
        if (enabled) {
            loadWorld(wm, foldername, env, enabled);
        }
    }

    public TweakWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean pvp, boolean allowMonsters, boolean allowAnimals, int viewdistance, boolean tooldurability, boolean enabled) {
        this.wm = wm;
        this.foldername = foldername;
        this.environment = env;
        this.pvp = pvp;
        this.allowanimals = allowAnimals;
        this.allowmonsters = allowMonsters;
        this.enabled = enabled;
        this.tooldurability = tooldurability;
        if (viewdistance > 3 && viewdistance < 16)
            this.viewdistance = viewdistance;
        else
            this.viewdistance = wm.getDefaultViewDistance();
        if (enabled) {
            loadWorld(wm, foldername, env, enabled);
        }
    }

    public TweakWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean allowMonsters, boolean allowAnimals, boolean enabled) {
        this.wm = wm;
        this.foldername = foldername;
        this.environment = env;
        this.allowanimals = allowAnimals;
        this.allowmonsters = allowMonsters;
        this.tooldurability = true;
        this.enabled = enabled;
        if (enabled) {
            loadWorld(wm, foldername, env, enabled);
        }
    }

    public void setDurabilityEnabled(boolean state) {
        if (wm.getPlugin().getConfigHandler().enablemod_InfDura)
            world.setToolDurability(state);
        else {
            wm.getPlugin().getLogger().severe("Tried to enable/disable tool durability for world " + world.getName() + ",");
            wm.getPlugin().getLogger().severe("But either your Bukkit is not modded, or you forgot to enable it in the config!");
        }

    }

    public Long getSeed(boolean netherseed) {
        if (netherseed && !nether_enabled) return null;
        if (netherseed) return nether.getSeed();

        return world.getSeed();
    }

    public void setSeed(long seed, boolean netherseed) {
        if (netherseed && !nether_enabled) return;
        if (netherseed) this.netherseed = seed;
        else this.seed = seed;
    }

    public Long getNetherseed() {
        return netherseed;
    }

    public void setNetherseed(Long netherseed) {
        this.netherseed = netherseed;
    }

    public Long getEndseed() {
        return endseed;
    }

    public void setEndseed(Long endseed) {
        this.endseed = endseed;
    }

    public ChunkGenerator getChunkGen() {
        return chunkgen;
    }

    public void setWorldType(WorldType type) {
        this.worldType = type;
    }

    public WorldType getWorldType() {
        return this.worldType;
    }

    public void setChunkGen(String chunkGen) {
        resolvePluginChunkGenerator(chunkGen);
    }

    public String getChunkGenClass() {
        return chunkGenClass;
    }

    public void setChunkGenClass(String chunkGenClass) {
        this.chunkGenClass = chunkGenClass;
    }

    public void resolvePluginChunkGenerator(String chunkGen) {
        String[] split = chunkGen.split(":");
        Plugin plugin = wm.getPlugin().getServer().getPluginManager().getPlugin(split[0]);
        if (plugin == null) {
            wm.getPlugin().getLogger().severe("Chunkgenerator error for " + world.getName() + ",");
            wm.getPlugin().getLogger().severe("Couldn't find plugin with name " + split[0] + "!");
            this.enabled = false;
            return;
        }

        chunkgen = plugin.getDefaultWorldGenerator(this.worldName, split.length > 1 ? split[1] : null);
    }

    public GameMode getGameMode() {
        if (this.gamemode == null)
            return GameMode.SURVIVAL;

        return gamemode;
    }

    public void setGameMode(GameMode mode) {
        this.gamemode = mode;
    }

    public boolean isDurabilityEnabled() {
        return world.getToolDurability();
    }

    public boolean isNetherEnabled() {
        return this.nether_enabled;
    }

    public boolean isTheEndEnabled() {
        return this.the_end_enabled;
    }

    public void loadWorld(WorldManager wm, String foldername, org.bukkit.World.Environment env, boolean enabled) {
        if (foldername.trim().equals("")) {

            this.enabled = false;
            world = null;
            worldName = null;
        } else {
            worldName = foldername.trim();
            WorldCreator worldCreator = new WorldCreator(worldName);
            if ((world = wm.getPlugin().getServer().getWorld(worldName)) == null) {
                // wm.getPlugin().getLogger().info("Creating new world!");
                environment = env;
                if (chunkgen != null)
                    worldCreator.generator(chunkgen);

                else if (chunkGenClass != null) {
                    try {
                        Class clazz = Class.forName(chunkGenClass);
                        if (clazz != null) {
                            Object c = clazz.newInstance();
                            if (c instanceof ChunkGenerator) {
                                ChunkGenerator cg = (ChunkGenerator) c;
                                if (c instanceof FlatGen) {
                                    FlatGen fg = (FlatGen) cg;
                                    int mh = wm.getPlugin().getConfigHandler().getGlobalconfig().getInt("worlds.extraworlds." + worldName + ".flatGen.mapHeight", 12);
                                    byte toplayer = (byte) wm.getPlugin().getConfigHandler().getGlobalconfig().getInt("worlds.extraworlds." + worldName + ".flatGen.toplayer", Material.GRASS.getId());
                                    byte normal = (byte) wm.getPlugin().getConfigHandler().getGlobalconfig().getInt("worlds.extraworlds." + worldName + ".flatGen.normal", Material.DIRT.getId());
                                    fg.setmapHeight(mh);
                                    fg.setNormal(normal);
                                    fg.setToplayer(toplayer);
                                    fg.assignWorldManager(wm);
                                    worldCreator.generator(fg);
                                } else {
                                    worldCreator.generator(cg);
                                }
                            } else {
                                this.wm.getPlugin().getLogger().info("Error in world " + worldName + "! " + chunkGenClass + " isn't a Chunk Generator class!");
                            }
                        }
                    } catch (ClassNotFoundException e) {
                        this.wm.getPlugin().getLogger().info("Error in world " + worldName + "! Can't find class with name " + chunkGenClass);
                        enabled = false;
                    } catch (InstantiationException e) {
                        enabled = false;
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        enabled = false;
                        e.printStackTrace();
                    }
                }

            } else {
                // wm.getPlugin().getLogger().info("This world already existed!");
                environment = world.getEnvironment();
            }

            if (seed != null) {
                wm.getPlugin().getLogger().info("World " + worldName + " Setting custom seed : "+seed);
                worldCreator.seed(seed);
            }

            WorldType type = getWorldType();
            if (type != null)
                worldCreator.type(type);

            if (enabled) {
                worldCreator.environment(env);

                world = worldCreator.createWorld();
                world.setKeepSpawnInMemory(this.getSpawnChunksActive());
                world.setPVP(pvp);
                world.setDifficulty(getDifficultyBukkit());
            }
            // TODO: Re-enable after it's been added again!
            // world.setViewDistance(this.viewdistance);
            this.setSpawnFlags(this.allowmonsters, this.allowmonsters);
            this.setDurabilityEnabled(tooldurability);
            this.enabled = true;
        }
    }

    public void loadWorld() {
        if (world != null) return;
        loadWorld(wm, foldername, environment, enabled);
    }

    public iWorld getWorld() {
        return this;
    }

    public org.bukkit.World.Environment getEnvironment() {
        return environment;
    }

    public void setSpawnChunksActive(boolean state) {
        // this.world.setKeepSpawnInMemory(state);
        if (world != null) this.world.setKeepSpawnInMemory(state);
        this.keepspawnactive = state;
    }

    public boolean isAllowFlight() {
        return allowFlight;
    }

    public void setAllowFlight(boolean allowFlight) {
        this.allowFlight = allowFlight;
    }

    public boolean getSpawnChunksActive() {
        return this.keepspawnactive;
    }

    public org.bukkit.World getBukkitWorld() {
        return world;
    }

    public int getPortalSearchWidth() {
        return this.portalSearchWidth;
    }

    public void setPortalSearchWidth(int searchWidth) {
        this.portalSearchWidth = searchWidth;
    }

    public World getNetherWorld() {
        if (!isEnabled()) return null;
        if (!isNetherEnabled()) return null;
        return nether;
    }

    public World getThe_endWorld() {
        if (!isEnabled()) return null;
        if (!isTheEndEnabled()) return null;
        return the_end;
    }

    public boolean getAllowAnimals() {
        return world.getAllowAnimals();
    }

    public void addNether() {
        if (!isEnabled()) return;
        WorldCreator wc = new WorldCreator(this.world.getName() + "_nether");
        World nw = wm.getPlugin().getServer().getWorld(this.world.getName() + "_nether");
        if (nw == null) {
            if (netherseed != null) wc.seed(netherseed);
            nw = wc.environment(World.Environment.NETHER).createWorld();
        }
        nw.setSpawnFlags(this.allowmonsters, this.allowanimals);
        nw.setToolDurability(this.tooldurability);

        this.nether_enabled = true;
        this.nether = nw;
    }

    public void addTheEnd() {
        if(!isEnabled()) return;
        WorldCreator wc = new WorldCreator(this.world.getName() + "_the_end");
        World endw = wm.getPlugin().getServer().getWorld(this.world.getName() + "_the_end");
        if(endw == null) {
            if(endseed != null) wc.seed(endseed);
            endw = wc.environment(World.Environment.THE_END).createWorld();
        }
        endw.setSpawnFlags(this.allowmonsters, this.allowanimals);
        endw.setToolDurability(this.tooldurability);

        this.the_end_enabled = true;
        this.the_end = endw;
    }

    public String getWorldName() {
        return worldName;
    }

    public void setAllowAnimals(boolean allowanimals) {
        this.world.setSpawnFlags(getAllowMonsters(), allowanimals);
    }

    public boolean getAllowMonsters() {
        return world.getAllowMonsters();
    }

    public void setAllowMonsters(boolean allowmonsters) {
        this.setSpawnFlags(allowmonsters, getAllowAnimals());
    }

    public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals) {
        this.allowanimals = allowAnimals;
        this.allowmonsters = allowMonsters;
        this.world.setSpawnFlags(allowMonsters, allowAnimals);
    }

    public String getName() {
        return worldName;
    }

    public boolean isEnabled() {
        return enabled;
    }

    public boolean isLoaded() {
        return world != null;
    }

    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    public void setMOTD(String[] lines) {
        this.motd = lines;
    }

    public String[] getMOTD() {
        return this.motd;
    }

    public boolean hasWorldMOTD() {
        return this.motd != null && this.motd.length > 0;
    }

    public void setPVP(boolean pvpallowed) {
        this.pvp = pvpallowed;
        this.world.setPVP(pvpallowed);
    }

    public boolean getPVP() {
        return this.pvp;
    }

    public int getViewDistance() {
        return viewdistance;
    }

    public void setViewDistance(int viewdistance) {
        this.viewdistance = viewdistance;
    }

    public YamlConfiguration getConfiguration() {
        /* File f = new File(wm.getPlugin().datafolder, "worlds/" + worldName + ".yml");
        if (!f.exists()) {
            try {
                f.mkdirs();
                f.createNewFile();
            } catch (IOException e) {
                wm.getPlugin().getLogger().info("Can't create world's config file!");
                return null;
            }
        }
        FileConfiguration config = new FileConfiguration(f);

        return config; */
        return null;
    }

    public int getDifficulty() {
        return difficulty;
    }

    public Difficulty getDifficultyBukkit() {
        return Difficulty.getByValue(difficulty);
    }

    public void setDifficulty(int difficulty) {
        this.difficulty = difficulty;
        if (world != null && isEnabled())
            world.setDifficulty(getDifficultyBukkit());
        if (nether != null && isEnabled())
            nether.setDifficulty(getDifficultyBukkit());
        if (the_end != null && isEnabled())
            the_end.setDifficulty(getDifficultyBukkit());
    }

    public void setDifficulty(Difficulty difficulty) {
        this.difficulty = difficulty.getValue();
        if (world != null && isEnabled())
            world.setDifficulty(getDifficultyBukkit());
        if (nether != null && isEnabled())
            nether.setDifficulty(getDifficultyBukkit());
        if (the_end != null && isEnabled())
            the_end.setDifficulty(getDifficultyBukkit());
    }
} 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;
    }
} public interface iWorld {
    public iWorld getWorld();
    public org.bukkit.World getBukkitWorld();
    public org.bukkit.World getNetherWorld();
    public org.bukkit.World getThe_endWorld();
    public int getPortalSearchWidth();
    public void setPortalSearchWidth(int searchWidth);
    public String getName();
    public void loadWorld();
    public ChunkGenerator getChunkGen();
    public void setChunkGen(String chunkGen);
    public String getChunkGenClass();
    public void setChunkGenClass(String chunkGenClass);
    public GameMode getGameMode();
    public void setGameMode(GameMode mode);
    public Long getSeed(boolean netherseed);
    public void setSeed(long seed, boolean netherseed);
    public boolean isDurabilityEnabled();
    public boolean isAllowFlight();
    public void setAllowFlight(boolean state);
    public void setDurabilityEnabled(boolean state);
    public void addNether();
    public boolean isNetherEnabled();
    public boolean isTheEndEnabled();
    public boolean isEnabled();
    public boolean isLoaded();
    public boolean getAllowAnimals();
    public String getWorldName();
    public void setAllowAnimals(boolean allowanimals);
    public boolean getAllowMonsters();
    public int getViewDistance();
    public void setViewDistance(int viewDistance);
    public void setPVP(boolean pvpallowed);
    public boolean getPVP();
    public void setAllowMonsters(boolean allowmonsters);
    public void setSpawnFlags(boolean allowMonsters, boolean allowAnimals);
    public void setEnabled(boolean enabled);
    public void setMOTD(String[] lines);
    public String[] getMOTD();
    public boolean hasWorldMOTD();
    public FileConfiguration getConfiguration();
    public int getDifficulty();
    public Difficulty getDifficultyBukkit();
    public void setDifficulty(int difficulty);
    public void setDifficulty(Difficulty difficulty);

}
import com.guntherdw.bukkit.tweakcraft.Chat.ChatHandler;
import com.guntherdw.bukkit.tweakcraft.Chat.ChatMode;
import com.guntherdw.bukkit.tweakcraft.Chat.Modes.AdminChat;
import com.guntherdw.bukkit.tweakcraft.Commands.aCommand;
import com.guntherdw.bukkit.tweakcraft.DataSources.Ban.BanHandler;
import com.guntherdw.bukkit.tweakcraft.Events.TweakcraftUtilsEvent;
import com.guntherdw.bukkit.tweakcraft.Exceptions.*;
import com.guntherdw.bukkit.tweakcraft.Packages.ItemDB;
import com.guntherdw.bukkit.tweakcraft.Tools.ArgumentParser;
import com.guntherdw.bukkit.tweakcraft.TweakcraftUtils;
import com.guntherdw.bukkit.tweakcraft.Worlds.TweakWorld;
import com.guntherdw.bukkit.tweakcraft.Worlds.WorldManager;
import com.guntherdw.bukkit.tweakcraft.Worlds.iWorld;
import org.bukkit.ChatColor;
import org.bukkit.Difficulty;
import org.bukkit.GameMode;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
                    }
                }
            }
        }


        return true;
    }

    private Difficulty parseDifficulty(String arg) {
        try { /* Try numbers first! */
            Integer d = Integer.parseInt(arg);
            return Difficulty.getByValue(d);
        } catch (NumberFormatException ex) { /* Try strings! */
            return Difficulty.valueOf(arg.toUpperCase());
        }
    }

    @aCommand(
        aliases = {"tweakcraft", "tc"},
        permissionBase = "tweakcraft",
        description = "General tweakcraftutils command",
        section = "admin"
    )
    public boolean tweakcraft(CommandSender sender, String command, String[] args)
        throws PermissionsException, CommandSenderException, CommandUsageException, CommandException {
        if (args.length > 0) {
            if (args[0].equalsIgnoreCase("version")) {
                sender.sendMessage(ChatColor.WHITE + plugin.getDescription().getName() + ": version " + ChatColor.GREEN + plugin.getDescription().getVersion());
            } else if (args[0].equalsIgnoreCase("reload")) {

                if (sender instanceof Player) {
                    if (!plugin.check((Player) sender, "reload")) {
                        sender.sendMessage(ChatColor.GREEN + "Not implemented yet!");
                        return true;
                    }
                }

                sender.sendMessage(ChatColor.GREEN + "Reloading settings,dbs and setting colors.");
                plugin.getConfigHandler().reloadConfig();
                BanHandler bh = plugin.getBanhandler();
                bh.reloadBans();
                ItemDB idb = plugin.getItemDB();
                idb.loadDataBase();
                for (Player p : plugin.getServer().getOnlinePlayers()) {
                    String name = p.getName();
                    // p.setDisplayName(plugin.getPlayerColor(name, false) + name + ChatColor.WHITE);
                    String displayName = plugin.getNickWithColors(p.getName());
                    String ldisplayname = displayName.substring(0, displayName.length() - 2);
                    p.setDisplayName(displayName);
                    if (ldisplayname.length() <= 16) {
                        try {
                            p.setPlayerListName(ldisplayname);
                        } catch (IllegalArgumentException ex) {
                            ;
                        }
                    }
                }
                WorldManager wm = plugin.getworldManager();
                for (String worldname : wm.getWorlds().keySet()) {
                    wm.loadMotd(worldname);
                }
                plugin.getPlayerListener().reloadInvisTable();
                /**
                 * This is handled by the config.reloadConfig() call.
                 */
                /* if(plugin.getConfigHandler().enablePersistence) {
                   plugin.getPlayerListener().reloadInfo();
               } */

                TweakcraftUtilsEvent reloadEvent = new TweakcraftUtilsEvent(TweakcraftUtilsEvent.Action.RELOAD);
                if (sender instanceof Player) reloadEvent.setPlayer((Player) sender);
                plugin.getServer().getPluginManager().callEvent(reloadEvent);

            } else if (args[0].equalsIgnoreCase("world")) {
                if (sender instanceof Player)
                    if (!plugin.check((Player) sender, "admin.world"))
                        throw new PermissionsException(command);
                if (args.length > 2) {
                    String modus = args[1];
                    String world = args[2];
                    String arg = args.length > 3 ? args[3] : null;
                    iWorld iw = plugin.getworldManager().getWorld(world);
                    if (modus.equalsIgnoreCase("unload")) {
                        if (iw != null) {
                            if (iw.isEnabled()) {
                                for (Player pl : iw.getBukkitWorld().getPlayers()) {
                                    pl.sendMessage(ChatColor.RED + "WORLD UNLOADING. Sending you to spawn on the first world.");
                                    pl.teleport(plugin.getServer().getWorlds().get(0).getSpawnLocation());
                                }
                                iw.setEnabled(false);
                                sender.sendMessage(ChatColor.GOLD + "Unloading world " + iw.getName());
                                plugin.getServer().unloadWorld(iw.getName(), arg == null || Boolean.parseBoolean(arg));
                            } else {
                                sender.sendMessage(ChatColor.RED + "That world wasn't enabled!");
                            }
                        } else {
                            sender.sendMessage(ChatColor.RED + "World error, is that world managed by TweakcraftUtils?");
                        }
                    } else if (modus.equalsIgnoreCase("create") || modus.equalsIgnoreCase("load")) {
                        if (arg == null) arg = "normal";
                        World.Environment env = null;
                        try {
                            env = World.Environment.valueOf(arg.toUpperCase());
                        } catch (IllegalArgumentException ex) {
                            env = World.Environment.NORMAL;
                        }
                        if (iw != null) {
                            if (iw.isEnabled()) {
                                sender.sendMessage(ChatColor.RED + "This world already is enabled!");
                            } else {
                                sender.sendMessage(ChatColor.GOLD + "Enabling world " + world + " with env " + env.name());
                                iw.loadWorld();
                                iw.setEnabled(true);
                            }
                        } else {
                            /**
                             * NEW WORLD
                             */
                            sender.sendMessage(ChatColor.GOLD + "Creating new world " + world + " with env " + env.name());
                            plugin.getworldManager().getWorlds().put(world, new TweakWorld(plugin.getworldManager(), world, env, true));