com.sijobe.console.GuiConsole.java Source code

Java tutorial

Introduction

Here is the source code for com.sijobe.console.GuiConsole.java

Source

package com.sijobe.console;

import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.minecraft.client.Minecraft;
import net.minecraft.network.NetHandlerPlayServer;
import net.minecraft.util.ChatAllowedCharacters;
import net.minecraft.client.gui.ChatLine;
import net.minecraft.client.entity.EntityClientPlayerMP;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.GuiIngame;
import net.minecraft.client.gui.GuiPlayerInfo;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.event.ServerChatEvent;

import com.kitsinger.console.MCConsole;
import com.kitsinger.console.cfg.ConfigGuiFactory;
import com.kitsinger.console.cfg.ConfigHandler;
import com.kitsinger.console.cfg.ConfigGui;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;

import com.vayner.console.ConsoleChatCommands;
import com.vayner.console.external.ExternalGuiConsole;

import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.GuiIngameModOptions;
import cpw.mods.fml.common.FMLCommonHandler;

//import net.minecraft.src.GuiModScreen;
//import com.vayner.console.guiapi.ConsoleSettings;

/**
 * @formatter:off
 *                TODO: P1 - Only save logs for the current world
 *                TODO: p1 - Per world / server configuration file
 *                TODO: P1 - Output filtering - allow blocking of certain text/people
 *                DONE: p2 - Text selection in the chat-history field (copy text)
 *                DONE: P2 - Spinner (tab auto complete) (more or less)
 *                TODO: P3 - Drop down menus?
 *                TODO: P2 - Improve look and feel
 *                TODO: P2 - Custom text color support. Holding CTRL then type a number will set the text to that color [0-f] - (0-15)
 *                TODO: P1 - Add ability to disable settings loader (in code) and ability to reset the settings ingame
 *                TODO: P3 - Dynamic settings screen, configure any setting in an easy to use GUI (partly complete)
 *                TODO: p2 - Improve text highlighting to be less buggy
 *                DONE: p1 - Add external window / console
 *                TODO: p1 - Add tab completion to external console
 *                TODO: p2 - Rewrite / improve text highlight system
 *                DONE: p1 - Fix message splitting incorrectly
 *                FIXME:p2 - Add option for 1 tick unpause - pause for singleplayer.
 *
 * @author simo_415, tellefma, SwooshyCueb
 *
 *         This program is free software: you can redistribute it and/or modify
 *         it under the terms of the GNU Lesser General Public License as published by
 *         the Free Software Foundation, either version 3 of the License, or
 *         (at your option) any later version.
 *
 *         This program is distributed in the hope that it will be useful,
 *         but WITHOUT ANY WARRANTY; without even the implied warranty of
 *         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *         GNU Lesser General Public License for more details.
 *
 *         You should have received a copy of the GNU General Public License
 *         along with this program. If not, see <http://www.gnu.org/licenses/>.
 * @formatter:on
 */
public class GuiConsole extends GuiScreen implements Runnable {
    /* @formatter:off */
    private String playername; // The name of the current player
    protected String message; // The current user input
    protected String input; // The current input line to draw (includes prefix)
    private int updateCounter; // The tick count - used for cursor blink rate
    private int slider; // Position of the scroll bar
    private int cursor; // Position of the cursor
    private int inputOffset; // Position in the message string where the input goes
    private int sliderHeight; // Height of the scroll bar
    private int currentChatWidth = 128; // Current chat space width
    private boolean isHighlighting; // Keeps track of the highlight mouse click
    private int[] firstHighlighting = new int[2]; // Position of the mouse (at character) initially for highlighting
    private int[] lastHighlighting = new int[2]; // Position of the mouse (at character) at end of highlighting
    private boolean isSliding; // Keeps track of the slider mouse click
    private int lastSliding; // Position of mouse at last frame for slider
    private int initialSliding; // Position of mouse initially for slider
    private int historyPosition; // Position of where in the history you are at
    private boolean isGuiOpen; // When the console is open this is true
    private boolean rebuildLines; // Keeps track of whether the lines list needs to be rebuilt
    private volatile Vector<String> log; // The log messages
    private SimpleDateFormat sdf; // The date format for logs

    private boolean pauseGame = true;
    private int pauseCountDown = 0;

    private int tabListPos; // Where you have tabbed to through word list
    private int tabMaxPos; // Max size of the list
    private boolean tabbing = false; // Is tabbing
    private boolean tabMatchPlayerNamesOnly = false; // Is matching for player names
    private int tabWordPos; // start place of tabWord
    private String tabMatchingWord; // The current word checking to
    private String tabMatchedWord; // The current word matched to
    private String tabBeforeCursor; // The string before the cursor when completing
    private String tabAfterCursor; // The string after the cursor when completing
    private ArrayList<String> tabCurrentList; // The current List matching words

    private volatile HashMap<String, String> keyBindings; // All the current key bindings
    private volatile List<Integer> keyDown; // List of all the keys currently held down
    private static boolean BACKGROUND_BINDING_EVENTS = false; // Allows the bindings to run ingame with different GUIs open

    private String logName; // The name of the log file to write
    private long lastWrite; // The time of the last log write

    private static final int CHARHEIGHT = 10; // Character height - used to quickly determine number of lines per view
    private static final String ALLOWED_CHARACTERS; // A list of permitted characters

    public static Vector<String> INPUT_HISTORY; // All the input which went into the console
    private static Vector<String> LINES; // All of the lines to output
    private static Vector<String> MESSAGES; // All of the input/output
    private static Vector<ConsoleListener> LISTENERS; // All of the console listeners which were registered

    private static int[] TOP; // Poor implementation to keep track of drawn scrollbar top button
    private static int[] BOTTOM; // Poor implementation to keep track of drawn scrollbar bottom button
    private static int[] BAR; // Poor implementation to keep track of drawn scrollbar
    private static int[] EXIT_BUTTON; // Poor implementation to keep track of drawn exit button
    private static int[] OPTION_BUTTON; // Poor implementation to keep track of drawn option button
    private static int[] EXTERNAL_BUTTON; // Poor implementation to keep track of drawn external console button
    private static int[] TEXT_BOX; // Poor implementation to keep track of drawn text box
    private static int[] HISTORY; // Poor implementation to keep track of drawn history field

    private static GuiConsole INSTANCE; // Instance of the class for singleton pattern

    /* @formatter:on */

    /**
     * Initialises all of the instance variables
     */
    static {
        if (!ConfigHandler.LOG_DIR.exists())
            ConfigHandler.LOG_DIR.mkdirs();

        ALLOWED_CHARACTERS = String.valueOf(ChatAllowedCharacters.allowedCharacters);
        MESSAGES = new Vector<String>();
        MESSAGES.add("\2476[MCC] Minecraft Console version: \2473" + MCConsole.VERSION
                + "\2476 for Minecraft version: \24731.4.4");
        MESSAGES.add("\2476Developers: \2472simo_415 \2476, \2474fsmv \2476and \2471tellefma");
        MESSAGES.add("");
        INPUT_HISTORY = new Vector<String>();
        LISTENERS = new Vector<ConsoleListener>();

        TOP = new int[4];
        BOTTOM = new int[4];
        BAR = new int[4];
        EXIT_BUTTON = new int[4];
        OPTION_BUTTON = new int[4];
        EXTERNAL_BUTTON = new int[4];
        TEXT_BOX = new int[4];

        INSTANCE = new GuiConsole();

        if (!ConfigHandler.MOD_DIR.exists()) {
            try {
                ConfigHandler.MOD_DIR.mkdirs();
            } catch (Exception e) {
            }
        }

        if (!ConfigHandler.LOG_DIR.exists()) {
            try {
                ConfigHandler.LOG_DIR.mkdirs();
            } catch (Exception e) {
            }
        }
    }

    /**
     * Constructor should only be initialised from within the class ( currently via static{ } )
     */
    private GuiConsole() {
        MCConsole.log.info("Trying to get username...");
        MCConsole.log.info("Try 1...");
        mc = Minecraft.getMinecraft();
        EntityPlayer player = mc.thePlayer;
        if (player != null) {
            playername = player.getDisplayName();
            MCConsole.log.info("Success? Fetched " + playername);
        } else {
            MCConsole.log.info("FAILED!");
        }
        MCConsole.log.info("Try 2...");
        playername = mc.getSession().getUsername();
        if (playername != null) {
            MCConsole.log.info("Success? Fetched " + playername);
        } else {
            MCConsole.log.info("FAILED!");
        }
        MCConsole.log.info("Try 3...");
        mc = FMLClientHandler.instance().getClient();
        player = mc.thePlayer;
        if (player != null) {
            playername = player.getDisplayName();
            MCConsole.log.info("Success? Fetched " + playername);
        } else {
            MCConsole.log.info("FAILED!");
        }
        MCConsole.log.info("Try 4...");
        playername = mc.getSession().getUsername();
        if (playername != null) {
            MCConsole.log.info("Success? Fetched " + playername);
        } else {
            MCConsole.log.info("FAILED!");
        }
        (new Thread(this)).start();
        isGuiOpen = false;
        log = new Vector<String>();
        sdf = new SimpleDateFormat(ConfigHandler.DATE_FORMAT_LOG);
        keyBindings = generateKeyBindings();
        keyDown = new Vector<Integer>();

        loadCoreCommands();
    }

    /**
     * Loads the core set of classes which handle player input/output.
     */
    private void loadCoreCommands() {
        addConsoleListener(new ConsoleSettingCommands());
        addConsoleListener(new ConsoleChatCommands());
        addConsoleListener(new ExternalGuiConsole());
    }

    /**
     * SingleTon pattern to get an instance of the GUI
     *
     * @return An instance of the GUI
     */
    public static GuiConsole getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new GuiConsole();
        }
        return INSTANCE;
    }

    /**
     * returns the current directory Minecraft console saves it files.
     * 
     * @return Minecraft console current mod directory
     */
    public static File getModDir() {
        return ConfigHandler.MOD_DIR;
    }

    /**
     * Generates a hashmap containing all of the configured key bindings from file
     *
     * @return A hashmap containing all the keybindings
     */
    public HashMap<String, String> generateKeyBindings() {
        Properties p = new Properties();
        HashMap<String, String> bindings = new HashMap<String, String>();
        try {
            p.load(new FileInputStream(new File(ConfigHandler.MOD_DIR, "bindings.properties")));
            Iterator i = p.keySet().iterator();
            while (i.hasNext()) {
                String o = (String) i.next();
                bindings.put(o, (String) p.get(o));
            }
        } catch (FileNotFoundException e) {
            System.out.println(
                    "[MCC] Could not find bindings.properties in " + ConfigHandler.MOD_DIR.getAbsolutePath());
        } catch (IOException e) {
            FMLCommonHandler.instance().raiseException(e, "something broke!", true);
        }
        return bindings;
    }

    /**
     * Rebuilds the line list so that the text input and slider can be correctly
     * rendered without missing parts and dynamically resize if required.
     */
    public void buildLines() {
        LINES = new Vector<String>();
        Vector<String> temp = (Vector<String>) MESSAGES.clone(); //There were problems with the run method modifying MESSAGES while this loop works. See issue #17
        for (String message : temp) {
            addLine(message);
        }
        rebuildLines = false;
    }

    /**
     * Adds a line to the line render list
     *
     * @param message - The line message to add
     */
    private void addLine(String message) {

        if (LINES == null) {
            buildLines();
        }

        if (message == null) {
            return;
        }

        //using minecraft's methods instead, seems to work fine for the moment
        LINES.addAll(mc.fontRenderer.listFormattedStringToWidth(message, currentChatWidth));

    }

    /**
     * Called when Minecraft initialises the GUI
     *
     * @see net.minecraft.src.GuiScreen#initGui()
     */
    @Override
    public void initGui() {
        Keyboard.enableRepeatEvents(true);
        isSliding = false;
        lastSliding = -1;
        slider = 0;
        initialSliding = 0;
        isHighlighting = false;
        clearHighlighting();
        cursor = 0;
        message = "";
        updateCounter = 0;
        historyPosition = 0;
        isGuiOpen = true;
        rebuildLines = true;
    }

    /**
     * Called when the GUI is closed by Minecraft - useful for cleanup
     *
     * @see net.minecraft.src.GuiScreen#onGuiClosed()
     */
    @Override
    public void onGuiClosed() {
        Keyboard.enableRepeatEvents(false);
        isGuiOpen = false;
    }

    /**
     * Called to update the screen on frame
     *
     * @see net.minecraft.src.GuiScreen#updateScreen()
     */
    @Override
    public void updateScreen() {
        updateCounter++;
    }

    @Override
    public boolean doesGuiPauseGame() {
        if (ConfigHandler.MISC_PASUE_GAME && ConfigHandler.CHAT_UNPASUE_PAUSE_WITH_MESSAGE && !pauseGame) {
            pauseGame = (pauseCountDown-- <= 0) ? true : false;
            return false;
        }
        return ConfigHandler.MISC_PASUE_GAME;
    }

    /**
     * Called when a key is typed, handles all input into the console
     *
     * @see net.minecraft.src.GuiScreen#keyTyped(char, int)
     */
    @Override
    protected void keyTyped(char key, int id) {
        // Multi key validation
        // Control + ?
        if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL)) {
            if (Keyboard.isKeyDown(Keyboard.KEY_C)) {
                if (firstHighlighting[0] != -1 && lastHighlighting[0] != -1) {
                    String clipboard = "";
                    int firstInLINES = firstHighlighting[0] <= lastHighlighting[0] ? firstHighlighting[0]
                            : lastHighlighting[0];

                    if (firstHighlighting[0] == lastHighlighting[0]) {
                        int firsti, lasti;
                        if (firstHighlighting[1] < lastHighlighting[1]) {
                            firsti = firstHighlighting[1];
                            lasti = lastHighlighting[1];
                        } else {
                            firsti = lastHighlighting[1];
                            lasti = firstHighlighting[1];
                        }
                        clipboard = LINES.get(firstInLINES).substring(firsti, lasti);
                    } else {
                        for (int i = 0; i < Math.abs(firstHighlighting[0] - lastHighlighting[0]); i++) {
                            String temp = LINES.get(firstInLINES + i);
                            if (firstInLINES + i == firstHighlighting[0]) {
                                if (firstHighlighting[0] < lastHighlighting[0]) {
                                    temp = temp.substring(firstHighlighting[1]);
                                } else {
                                    temp = temp.substring(0, firstHighlighting[1]);
                                }
                            } else if (firstInLINES + i == lastHighlighting[0]) {
                                if (firstHighlighting[0] > lastHighlighting[0]) {
                                    temp = temp.substring(lastHighlighting[1]);
                                } else {
                                    temp = temp.substring(0, lastHighlighting[1]);
                                }
                            }

                            clipboard += temp + " ";
                        }
                    }

                    setClipboardString(clipboard.trim());
                } else {
                    if (lastHighlighting[1] != firstHighlighting[1] && firstHighlighting[0] == -1
                            && lastHighlighting[0] == -1) {
                        if (firstHighlighting[1] < lastHighlighting[1]) {
                            setClipboardString(message.substring(firstHighlighting[1], lastHighlighting[1]));
                        } else {
                            setClipboardString(message.substring(lastHighlighting[1], firstHighlighting[1]));
                        }
                    }
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_V)) {
                paste();
            } else if (Keyboard.isKeyDown(Keyboard.KEY_X)) {
                if (firstHighlighting[1] != lastHighlighting[1] && firstHighlighting[0] == -1
                        && lastHighlighting[0] == -1) {
                    String start, end;
                    if (firstHighlighting[1] < lastHighlighting[1]) {
                        setClipboardString(message.substring(firstHighlighting[1], lastHighlighting[1]));
                        start = message.substring(0, firstHighlighting[1]);
                        end = message.substring(lastHighlighting[1]);
                    } else {
                        setClipboardString(message.substring(lastHighlighting[1], firstHighlighting[1]));
                        start = message.substring(0, lastHighlighting[1]);
                        end = message.substring(firstHighlighting[1]);
                    }

                    message = start + end;
                    firstHighlighting[0] = lastHighlighting[0] = -1;
                    firstHighlighting[1] = lastHighlighting[1] = 0;
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
                if (!ConfigHandler.EMACS_KEYS) {
                    if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
                        firstHighlighting[1] = 0;
                        lastHighlighting[1] = message.length();
                    } else {
                        firstHighlighting[0] = 0;
                        firstHighlighting[1] = 0;
                        lastHighlighting[0] = LINES.size() - 1;
                        lastHighlighting[1] = LINES.get(LINES.size() - 1).length();
                    }
                } else {
                    // go to beginning of line
                    cursor = 0;
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_E)) {
                if (ConfigHandler.EMACS_KEYS) {
                    // go to end of line
                    cursor = message.length();
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_K)) {
                if (ConfigHandler.EMACS_KEYS && firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
                    // Cut to end of line
                    setClipboardString(message.substring(cursor, message.length()));

                    message = message.substring(0, cursor);
                    clearHighlighting();
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_Y)) {
                if (ConfigHandler.EMACS_KEYS) {
                    paste();
                }
            } else if (Keyboard.isKeyDown(Keyboard.KEY_D)) {
                if (ConfigHandler.EMACS_KEYS) {
                    delete();
                }
            }
            return;
        }

        if (tabbing) {
            if (id == ConfigHandler.KEY_AUTONEXT) {
                updateTabPos(1);
                return;
            } else if (id == ConfigHandler.KEY_AUTOPREV) {
                updateTabPos(-1);
                return;
            }
        }

        if (id != ConfigHandler.KEY_AUTOCOMPLETE && id != ConfigHandler.KEY_AUTONEXT
                && id != ConfigHandler.KEY_AUTOPREV && id != Keyboard.KEY_BACK) {
            resetTabbing();
        } else if (id == ConfigHandler.KEY_AUTOCOMPLETE) {
            clearHighlighting();
            /*if (message.startsWith("@get ") || message.startsWith("@list ") || message.startsWith("@set ")) {
               String[] str = message.split(" ");
                
               String match = "";
                
               if (str.length > 1) {
                  if (tabListPos == 0) {
              match = str[1];
                  } else {
              match = tabMatchingWord;
                  }
               }
               if (tabListPos < 0) {
                  tabListPos = 0;
               }
                
               if (cursor >= str[0].length() + 1 && cursor <= str[0].length() + 1 + match.length() || tabListPos > 0) {
                  ArrayList<String> tempList = new ArrayList<String>(Arrays.asList(ConsoleSettingCommands.list("").split("\n")));
                  ArrayList<String> list = new ArrayList<String>();
                  for (int i = 0; i < tempList.size(); i++) {
              if (tempList.get(i).startsWith(match.toUpperCase())) {
                 list.add(tempList.get(i)); //Can't delete from a list in a loop; workaround
              }
                  }
                
                  if (list.size() > 0) {
              tabMatchingWord = match;
                
              if (tabListPos == 0) {
                 message = message.substring(0, str[0].length() + 1) + list.get(tabListPos) + message.substring(str[0].length() + 1 + match.length(), message.length());
              } else if (tabListPos > 0) {
                 message = message.substring(0, str[0].length() + 1) + list.get(tabListPos) + message.substring(str[0].length() + 1 + list.get(tabListPos - 1).length(), message.length());
              }
              cursor = str[0].length() + 1 + list.get(tabListPos).length();
                
              tabListPos++;
              if (tabListPos >= list.size()) {
                 tabListPos = -1;
              }
                  }
               }
            } else {*/
            updateTabPos(1);
            //}
            return;
        }

        // Single key validation
        switch (id) {
        case Keyboard.KEY_ESCAPE:
            // Exits the GUI
            mc.displayGuiScreen(null);
            break;

        case Keyboard.KEY_RETURN:
            // Submits the message
            String s = message.trim();

            if (s.length() > 0) {
                addInputMessage(s);
            }

            if (ConfigHandler.CLOSE_ON_SUBMIT) {
                mc.displayGuiScreen(null);
            }

            if (ConfigHandler.SCROLL_TO_BOTTOM_ON_SUBMIT) {
                slider = 0;
            }

            message = "";
            cursor = 0;
            inputOffset = 0;
            historyPosition = 0;
            clearHighlighting();
            resetTabbing();
            break;

        case Keyboard.KEY_LEFT:
            // Moves the cursor left
            if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
                if (firstHighlighting[1] == lastHighlighting[1]) {
                    if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
                        firstHighlighting[1] = cursor;
                        lastHighlighting[1] = cursor;
                    }
                }

                lastHighlighting[1]--;
                if (lastHighlighting[1] < 0) {
                    lastHighlighting[1] = 0;
                    if (lastHighlighting[0] > 0) {
                        lastHighlighting[0]--;
                    }
                }
                validateHighlighting();
            } else {
                clearHighlighting();
            }
            cursor--;
            break;

        case Keyboard.KEY_RIGHT:
            // Moves the cursor right
            if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) {
                if (firstHighlighting[1] == lastHighlighting[1]) {
                    if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
                        firstHighlighting[1] = cursor;
                        lastHighlighting[1] = cursor;
                    }
                }

                lastHighlighting[1]++;
                if (lastHighlighting[0] == -1 && firstHighlighting[0] == -1) {
                    if (lastHighlighting[1] > message.length()) {
                        lastHighlighting[1] = message.length();
                    }
                } else {
                    if (lastHighlighting[1] > LINES.get(lastHighlighting[0]).length()) {
                        lastHighlighting[1] = LINES.get(lastHighlighting[0]).length();
                        if (lastHighlighting[0] < LINES.size() - 1) {
                            lastHighlighting[0]++;
                        }
                    }
                }
                validateHighlighting();
            } else {
                clearHighlighting();
            }
            cursor++;
            break;

        case Keyboard.KEY_DOWN:
            // Moves the history position down
            message = getInputHistory(--historyPosition);
            cursor = message.length();
            clearHighlighting();
            break;

        case Keyboard.KEY_UP:
            // Moves the history position down
            message = getInputHistory(++historyPosition);
            cursor = message.length();
            clearHighlighting();
            break;

        case Keyboard.KEY_DELETE:
            resetTabbing();
            delete();
            break;

        case Keyboard.KEY_BACK:
            // Backspace
            if (tabbing) {
                message = message.substring(0, tabWordPos) + tabMatchingWord;
                resetTabbing();
                break;
            }
            if (message.length() > 0) {
                if (firstHighlighting[1] == lastHighlighting[1] || firstHighlighting[0] != -1
                        || lastHighlighting[0] != -1) {
                    validateCursor();
                    String start = message.substring(0, cursor);
                    String end = message.substring(cursor, message.length());
                    this.message = start.substring(0, (start.length() - 1 > -1 ? start.length() - 1 : 0)) + end;
                    cursor--;
                    inputOffset--;
                    if (inputOffset < 0) {
                        inputOffset = 0;
                    }
                } else {
                    String start, end;
                    if (firstHighlighting[1] < lastHighlighting[1]) {
                        start = message.substring(0, firstHighlighting[1]);
                        end = message.substring(lastHighlighting[1]);
                    } else {
                        start = message.substring(0, lastHighlighting[1]);
                        end = message.substring(firstHighlighting[1]);
                    }
                    inputOffset -= Math.abs(lastHighlighting[1] - firstHighlighting[1]);
                    if (inputOffset < 0) {
                        inputOffset = 0;
                    }
                    message = start + end;
                }
                clearHighlighting();
            }
            break;
        case Keyboard.KEY_HOME:
            cursor = 0;
            clearHighlighting();
            break;
        case Keyboard.KEY_END:
            cursor = message.length();
            clearHighlighting();
            break;
        default:
            resetTabbing();
            // Verifies that the character is in the character set before adding
            if (updateCounter != 0) {
                if (ConfigHandler.CLOSE_WITH_OPEN_KEY && id == MCConsole.openKey.getKeyCode()) {
                    MCConsole.closeConsole();
                    break;
                }
                if (ALLOWED_CHARACTERS.indexOf(key) >= 0
                        && this.message.length() < ConfigHandler.CHAT_INPUT_LENGTH_MAX && !(message.startsWith("/")
                                && message.length() > ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX - 1)) {
                    insertChar(key);
                }
            }
        }
    }

    private void insertChar(char key) {
        if (firstHighlighting[1] == lastHighlighting[1] || firstHighlighting[0] != -1 || lastHighlighting[1] != 1) {
            validateCursor();
            clearHighlighting();
            String start = message.substring(0, cursor);
            String end = message.substring(cursor, message.length());
            this.message = start + key + end;
            cursor++;
        } else {
            String start, end;
            if (firstHighlighting[1] < lastHighlighting[1]) {
                start = message.substring(0, firstHighlighting[1]);
                end = message.substring(lastHighlighting[1]);
            } else {
                start = message.substring(0, lastHighlighting[1]);
                end = message.substring(firstHighlighting[1]);
            }

            message = start + key + end;
            cursor = start.length() + 1;
            clearHighlighting();
        }
    }

    private int highlightMoveEnd(int diff) {
        //TODO fill out and rearrange code
        return 0;
    }

    private int highlightSetEnd(int line, int pos) {
        //TODO fill out and rearrange code
        return 0;
    }

    /**
     * Resets tabbing values an progress
     */
    private void resetTabbing() {
        tabbing = false;
        tabMatchPlayerNamesOnly = false;
        tabListPos = 0;
        tabWordPos = 0;
        tabMatchingWord = "";
        tabMatchedWord = "";
    }

    /**
     * Updates / starts the tabbing progress with appropriate offset
     * @param Diff is the offset value for the list
     */
    private void updateTabPos(int Diff) {
        if (tabbing == false) {
            //find the last word, defined via the cursor
            tabBeforeCursor = message.substring(0, cursor);
            tabAfterCursor = (cursor < message.length()) ? message.substring(cursor) : "";

            if (tabBeforeCursor == null || tabBeforeCursor.length() == 0) {
                tabMatchingWord = "";
                tabWordPos = 0;
            } else if (tabBeforeCursor.startsWith("@")) {
                tabMatchingWord = tabBeforeCursor.substring(1);
                tabWordPos = 1;
                tabMatchPlayerNamesOnly = true;
            } else if (tabBeforeCursor.endsWith(" ")) {
                tabMatchingWord = "";
                tabWordPos = tabBeforeCursor.length();
            } else if (tabBeforeCursor.contains(" ")) {
                String[] splitMessage = tabBeforeCursor.split(" ");
                tabMatchingWord = splitMessage[splitMessage.length - 1];
                tabWordPos = tabBeforeCursor.length() - tabMatchingWord.length();
            } else {
                tabMatchingWord = tabBeforeCursor;
                tabWordPos = 0;
            }
        }

        List<String> autoWords;

        if (tabMatchPlayerNamesOnly)
            autoWords = getPlayerNames(); //list of player names only
        else
            autoWords = getAutoPossibility(); //list of all possible words

        if (autoWords == null)
            return;

        tabCurrentList = new ArrayList<String>();
        tabMaxPos = 0;
        if (tabMatchingWord == null) {
            tabMaxPos = autoWords.size();
            tabCurrentList.addAll(autoWords);
        } else {
            for (int i = 0; i < autoWords.size(); i++) {
                String currentWord = autoWords.get(i);
                // Tests if a autoword starts with the matching word
                if (currentWord.toLowerCase().startsWith(tabMatchingWord.toLowerCase())) {
                    tabCurrentList.add(currentWord);
                }
            }
            tabMaxPos = tabCurrentList.size();
        }

        if (tabCurrentList.size() > 0) {

            if (tabbing)
                tabListPos += Diff;
            else
                tabbing = true;

            //check for see if out of bound
            tabListPos = (tabListPos >= tabMaxPos) ? 0 : tabListPos;
            tabListPos = (tabListPos < 0) ? tabMaxPos - 1 : tabListPos;

            //tabListPos = (tabListPos > tabMaxPos)? tabMaxPos : tabListPos; //if player leaves whiles browsing words
            tabMatchedWord = tabCurrentList.get(tabListPos);

            if (message.length() > 0)
                message = message.substring(0, tabWordPos) + tabMatchedWord + tabAfterCursor;
            else
                message = tabMatchedWord;

            cursor = message.length();
        }
    }

    /**
     * Returns true if the current game is being player on a server
     *
     * @return True is returned when the current game is being played on a
     *         Minecraft server
     */
    public boolean isMultiplayerMode() {
        return !mc.isSingleplayer();
    }

    /**
     * Returns true/false depending on if the integrated server is running
     * 
     * @return True if the integrated server is running.
     */
    public boolean isLocalMultiplayerServer() {
        return mc.isIntegratedServerRunning();
    }

    /**
     * 
     * @link{ GuiConsole.cleanString }
     * @return cleaned servername or ""
     */
    // mc.func_147104_D() = mc.getServerData()
    public String getServerName() {
        if (isMultiplayerMode()) {
            String name = mc.func_147104_D().serverName;
            return (name.equals(null)) ? "" : cleanString(name);
        }
        return "";
    }

    public String getServerIp() {
        if (isMultiplayerMode())
            return mc.func_147104_D().serverIP;
        return "";
    }

    /**
     * Gets all the usernames on the current server you're on
     *
     * @return A list in alphabetical order of players logged onto the server
     */
    public List<String> getPlayerNames() {
        List<String> names = new ArrayList<String>();
        if (isMultiplayerMode() && mc.thePlayer instanceof EntityClientPlayerMP) {
            NetHandlerPlayClient netclienthandler = ((EntityClientPlayerMP) mc.thePlayer).sendQueue;
            List<GuiPlayerInfo> tempList = netclienthandler.playerInfoList;
            for (GuiPlayerInfo info : (List<GuiPlayerInfo>) tempList) {
                String name = info.name; //There were some problems with bukkit plugins adding prefixes or suffixes to the names list. This cleans the strings.
                Pattern pattern = Pattern.compile("[\\[[\\{[\\(]]]+?.*?[\\][\\}[\\)]]]"); //Get rid of everything between the brackets (), [], or {}
                Matcher matcher = pattern.matcher(name);
                name = matcher.replaceAll("");
                String cleanName = "";
                for (int i = 0; i < name.length(); i++) { //Get rid of every invalid character for minecraft usernames
                    if (name.charAt(i) == '\u00a7') { //Gets rid of color codes
                        i++;
                        continue;
                    }

                    if (Character.isLetterOrDigit(name.charAt(i)) || name.charAt(i) == '_') {
                        cleanName += name.charAt(i);
                    }
                }
                if (!cleanName.equals("")) {
                    names.add(cleanName);
                }
            }
        } else {
            names.add(playername);
        }
        return names;
    }

    /**
     * Gets all possible autocomplete words, including playernames
     *
     * @return A list of all possible word completions for autoword
     */

    public List<String> getAutoPossibility() {
        List<String> autowords = new ArrayList<String>();
        List<String> players = getPlayerNames();
        if (players != null && players.size() > 0) {
            autowords.addAll(players);
        }

        autowords.addAll(ConsoleChatCommands.getChatCommands());

        return autowords;
    }

    /**
     * Changes the highlighting bounds so nothing is highlighted
     */

    public void clearHighlighting() {
        lastHighlighting[0] = -1;
        lastHighlighting[1] = 0;
        firstHighlighting[0] = -1;
        firstHighlighting[1] = 0;
    }

    /**
     * Cleans a dirty string of any invalid characters then returns the clean
     * string to the user. Verifies that the string doesn't go beyond the maximum
     * length as well
     *
     * @param dirty - The string to clean
     * @return A nice clean string
     */
    public static String cleanString(String dirty) {
        String clean = "";
        if (dirty == null) {
            return "";
        }
        char letters[] = dirty.toCharArray();
        for (char letter : letters) {
            if (ALLOWED_CHARACTERS.indexOf(letter) >= 0) {
                clean += letter;
            }
        }
        if (clean.length() >= ConfigHandler.CHAT_INPUT_LENGTH_MAX) {
            clean = clean.substring(0, ConfigHandler.CHAT_INPUT_LENGTH_MAX - 1);
        }
        return clean;
    }

    /**
     * Gets the String on the system clip board, if it exists. Otherwise null is
     * returned
     *
     * @return Returns the String on the clip board
     */
    public static String getClipboardString() {
        try {
            Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
            if (t != null && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                return cleanString((String) t.getTransferData(DataFlavor.stringFlavor));
            }
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Sets a String onto the system clip board.
     *
     * @param str - The string to copy onto the clip board
     */
    public static void setClipboardString(String str) {
        try {
            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(str), null);
        } catch (Exception e) {
        }
    }

    public static String getInputPrefix() {
        return ConfigHandler.CHAT_INPUT_PREFIX;
    }

    /**
     * Command history implementation, sets the historyPosition pointer to
     * position based on its validity. The validity is verified by the method
     * and the pointer is kept between the bounds of the history
     *
     * @param position - The position to set the pointer to
     * @return The input at this position in history
     */
    private String getInputHistory(int position) {
        if (INPUT_HISTORY.size() == 0) {
            return "";
        }
        if (position <= 0) {
            position = 0;
            return "";
        }
        if (position > INPUT_HISTORY.size()) {
            position = INPUT_HISTORY.size();
        }
        historyPosition = position;
        return INPUT_HISTORY.elementAt(INPUT_HISTORY.size() - historyPosition);
    }

    /**
     * Validates that the cursor is in a valid position, if the cursor isn't
     * then the cursor is moved into the closest valid position.
     */
    private void validateCursor() {
        if (cursor > message.length()) {
            cursor = message.length();
        } else if (cursor < 0) {
            cursor = 0;
        }
    }

    /**
     * Sets the inputOffset value to the appropriate place
     */

    private void validateOffset() {
        String start = message.substring(0, cursor);
        String end = message.substring(cursor, message.length());
        input = ConfigHandler.CHAT_INPUT_PREFIX + start + ((updateCounter / 8) % 2 != 0 ? "." : "!") + end;

        if (mc.fontRenderer.getStringWidth(input) >= TEXT_BOX[2] - TEXT_BOX[0]
                - ConfigHandler.SCREEN_BORDERSIZE * 2) {
            int upperbound = input.length();
            int boxsize = TEXT_BOX[2] - TEXT_BOX[0] - ConfigHandler.SCREEN_BORDERSIZE * 2;

            if (inputOffset < 0) {
                inputOffset = 0;
            }

            if (inputOffset > ConfigHandler.CHAT_INPUT_PREFIX.length()) {
                while (cursor < inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length() && inputOffset > 0) {
                    inputOffset--;
                }
            } else {
                while (cursor < inputOffset && inputOffset > 0) {
                    inputOffset--;
                }
            }

            while (mc.fontRenderer.getStringWidth(input.substring(inputOffset,
                    cursor + ConfigHandler.CHAT_INPUT_PREFIX.length() + 1)) >= boxsize) {
                inputOffset++;
            }

            while (mc.fontRenderer.getStringWidth(input.substring(inputOffset, upperbound)) >= boxsize) {
                upperbound--;
            }

            if (upperbound > input.length()) {
                upperbound = input.length();
            }
            input = input.substring(inputOffset, upperbound);
        }
    }

    /**
     * Makes sure the highlighting values are within the string bounds
     */
    private void validateHighlighting() {
        if (firstHighlighting[0] < -1) {
            firstHighlighting[0] = -1;
        } else if (firstHighlighting[0] > LINES.size()) {
            firstHighlighting[0] = LINES.size();
        }

        if (lastHighlighting[0] < -1) {
            lastHighlighting[0] = -1;
        } else if (lastHighlighting[0] > LINES.size()) {
            lastHighlighting[0] = LINES.size();
        }

        if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
            if (lastHighlighting[1] < 0) {
                lastHighlighting[1] = 0;
            } else if (lastHighlighting[1] > message.length()) {
                lastHighlighting[1] = message.length();
            }

            if (firstHighlighting[1] < 0) {
                firstHighlighting[1] = 0;
            } else if (firstHighlighting[1] > message.length()) {
                firstHighlighting[1] = message.length();
            }
        } else {
            if (lastHighlighting[1] < 0) {
                lastHighlighting[1] = 0;
            } else if (lastHighlighting[1] > LINES.get(lastHighlighting[0]).length()) {
                lastHighlighting[1] = LINES.get(lastHighlighting[0]).length();
            }

            if (firstHighlighting[1] < 0) {
                firstHighlighting[1] = 0;
            } else if (firstHighlighting[1] > LINES.get(firstHighlighting[0]).length()) {
                firstHighlighting[1] = LINES.get(firstHighlighting[0]).length();
            }
        }
    }

    /**
     * Paste clipboard at cursor position.
     */
    private void paste() {
        String clipboard = getClipboardString();
        if (clipboard != null) {
            if (lastHighlighting[1] == firstHighlighting[1] || firstHighlighting[0] != -1
                    || lastHighlighting[0] != -1) {
                String start = "";
                String end = "";
                validateCursor();

                if (message != null && message.length() > 0) {
                    start = message.substring(0, cursor);
                    end = message.substring(cursor);
                }

                int limit = ConfigHandler.CHAT_INPUT_LENGTH_MAX - message.length();
                if (limit < clipboard.length()) {
                    clipboard = clipboard.substring(0, limit);
                }

                message = start + clipboard + end;
                cursor = (start + clipboard).length() + 1;
            } else {
                String start, end;
                if (firstHighlighting[1] < lastHighlighting[1]) {
                    start = message.substring(0, firstHighlighting[1]);
                    end = message.substring(lastHighlighting[1]);
                } else {
                    start = message.substring(0, lastHighlighting[1]);
                    end = message.substring(firstHighlighting[1]);
                }

                int limit = ConfigHandler.CHAT_INPUT_LENGTH_MAX - message.length();
                if (limit < clipboard.length()) {
                    clipboard = clipboard.substring(0, limit);
                }

                message = start + clipboard + end;
                cursor = (start + clipboard).length() + 1;
                clearHighlighting();
            }
        }
    }

    /**
     * Delete the next character.
     */
    private void delete() {
        // Delete
        if (message.length() > 0) {
            if (firstHighlighting[1] == lastHighlighting[1] || firstHighlighting[0] != -1
                    || lastHighlighting[0] != -1) {
                validateCursor();
                String start = message.substring(0, cursor);
                String end = message.substring(cursor, message.length());
                this.message = start + (end.length() > 0 ? end.substring(1) : end);
            } else if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1) {
                String start, end;
                if (firstHighlighting[1] < lastHighlighting[1]) {
                    start = message.substring(0, firstHighlighting[1]);
                    end = message.substring(lastHighlighting[1]);
                } else {
                    start = message.substring(0, lastHighlighting[1]);
                    end = message.substring(firstHighlighting[1]);
                }
                message = start + end;
            }
            clearHighlighting();
        }
    }

    /**
     * Called per frame to draw the new frame
     *
     * @see net.minecraft.src.GuiScreen#drawScreen(int, int, float)
     */
    @Override
    public void drawScreen(int mousex, int mousey, float f) {
        // Background
        int minx = ConfigHandler.SCREEN_PADDING_LEFT;
        int miny = ConfigHandler.SCREEN_PADDING_TOP;
        int maxx = width - ConfigHandler.SCREEN_PADDING_RIGHT;
        int maxy = height - ConfigHandler.SCREEN_PADDING_BOTTOM;
        drawRect(minx, miny, maxx, maxy, ConfigHandler.COLOR_BASE);

        // Input Text box
        int textbox_minx = minx + ConfigHandler.SCREEN_BORDERSIZE;
        int textbox_maxx = maxx - ConfigHandler.SCREEN_BORDERSIZE;
        int textbox_miny = maxy - CHARHEIGHT - ConfigHandler.SCREEN_BORDERSIZE;
        int textbox_maxy = maxy - ConfigHandler.SCREEN_BORDERSIZE;

        if (ConfigHandler.SCREEN_MESSAGE_LENGHT_DISPLAY) {

            String currentChars = String.valueOf(message.length());
            String maxLenght = String.valueOf(ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX);
            int charDiff = maxLenght.length() - currentChars.length();
            for (int i = 0; i < charDiff; i++) {
                currentChars = "0" + currentChars;
            }

            String messageLenght = currentChars + "/" + maxLenght;

            int indent = mc.fontRenderer.getStringWidth(messageLenght) + ConfigHandler.SCREEN_BORDERSIZE * 2;

            int messageLenghtBox_maxx = textbox_maxx;
            int messageLenghtBox_minx = textbox_maxx - indent + ConfigHandler.SCREEN_BORDERSIZE;

            drawRect(messageLenghtBox_minx, textbox_miny, messageLenghtBox_maxx, textbox_maxy,
                    ConfigHandler.COLOR_MESSAGE_LENGTH_BACKGROUND);

            drawRect(textbox_minx, textbox_miny, textbox_maxx - indent, textbox_maxy,
                    ConfigHandler.COLOR_INPUT_BACKGROUND);
            TEXT_BOX = new int[] { textbox_minx, textbox_miny, textbox_maxx - indent, textbox_maxy };
            drawString(mc.fontRenderer, messageLenght, messageLenghtBox_minx + 1, textbox_miny + 1,
                    ConfigHandler.COLOR_INPUT_TEXT);

        } else {
            drawRect(textbox_minx, textbox_miny, textbox_maxx, textbox_maxy, ConfigHandler.COLOR_INPUT_BACKGROUND);
            TEXT_BOX = new int[] { textbox_minx, textbox_miny, textbox_maxx, textbox_maxy };
        }

        // Input text highlighting
        if (firstHighlighting[0] == -1 && lastHighlighting[0] == -1
                && lastHighlighting[1] != firstHighlighting[1]) {
            int firstH, lastH; //First letter position, last letter position
            if (firstHighlighting[1] < lastHighlighting[1]) {
                firstH = firstHighlighting[1];
                lastH = lastHighlighting[1];
            } else {
                firstH = lastHighlighting[1];
                lastH = firstHighlighting[1];
            }

            if (firstH < 0) {
                firstH = 0;
            }

            if (lastH > message.length()) {
                lastH = message.length();
            }

            String messageSection;
            validateCursor();
            validateOffset();
            validateHighlighting();
            if (inputOffset < ConfigHandler.CHAT_INPUT_PREFIX.length()) {
                messageSection = (ConfigHandler.CHAT_INPUT_PREFIX + message.substring(0, firstH))
                        .substring(inputOffset);
            } else {
                messageSection = message
                        .substring((inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length()) > firstH ? firstH
                                : inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length(), firstH);
            }

            int highlighting_minx = 1 + TEXT_BOX[0] + mc.fontRenderer.getStringWidth(messageSection);

            if (firstH < inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length()) {
                messageSection = message.substring(inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length(), lastH);
            } else {
                messageSection = message.substring(firstH, lastH);
            }

            int highlighting_maxx = 1 + highlighting_minx + mc.fontRenderer.getStringWidth(messageSection);
            int highlighting_miny = TEXT_BOX[1];
            int highlighting_maxy = highlighting_miny + CHARHEIGHT;
            int ExclamationStringWidth = mc.fontRenderer.getStringWidth("!");

            if (cursor > firstH && cursor < lastH)
                highlighting_maxx += ExclamationStringWidth;
            else if (cursor <= firstH) {
                highlighting_minx += ExclamationStringWidth;
                highlighting_maxx += ExclamationStringWidth;
            }

            if (highlighting_maxx > TEXT_BOX[2]) {
                highlighting_maxx = TEXT_BOX[2];
            }

            drawRect(highlighting_minx, highlighting_miny, highlighting_maxx, highlighting_maxy,
                    ConfigHandler.COLOR_TEXT_HIGHLIGHT);
        }

        // Past messages - dialog
        int message_miny = miny + ConfigHandler.SCREEN_BORDERSIZE;
        int message_maxy = textbox_miny - ConfigHandler.SCREEN_BORDERSIZE;
        int chatTemp = maxx - (ConfigHandler.SCREEN_BORDERSIZE * 2) - 10 - textbox_minx;
        if (currentChatWidth != chatTemp) {
            currentChatWidth = chatTemp;
            buildLines();
        }

        HISTORY = new int[] { textbox_minx, message_miny, textbox_minx + currentChatWidth, message_maxy };

        if (LINES == null || rebuildLines) {
            buildLines();
        }

        drawRect(textbox_minx, message_miny, textbox_minx + currentChatWidth, message_maxy,
                ConfigHandler.COLOR_OUTPUT_BACKGROUND);

        // Past messages - highlighting

        if (firstHighlighting[0] > -1 && lastHighlighting[0] > -1
                && !Arrays.equals(firstHighlighting, lastHighlighting)) {
            int maxDisplayedLines = (HISTORY[3] - HISTORY[1]) / (CHARHEIGHT - 1);
            int linesDisplayed = LINES.size() >= maxDisplayedLines ? maxDisplayedLines : LINES.size();
            int lineAtOnScreen_i;
            if (linesDisplayed < maxDisplayedLines) {
                lineAtOnScreen_i = maxDisplayedLines - LINES.size() + firstHighlighting[0];
            } else {
                lineAtOnScreen_i = firstHighlighting[0] - LINES.size() + linesDisplayed + slider;
            }

            int lineAtOnScreen_f;
            if (linesDisplayed < maxDisplayedLines) {
                lineAtOnScreen_f = maxDisplayedLines - LINES.size() + lastHighlighting[0];
            } else {
                lineAtOnScreen_f = lastHighlighting[0] - LINES.size() + linesDisplayed + slider;
            }

            int[] rect = new int[4];
            int xoffset = HISTORY[0] + ConfigHandler.SCREEN_BORDERSIZE;
            int yoffset = HISTORY[1] + ConfigHandler.SCREEN_BORDERSIZE;

            //initial
            int h_minx = xoffset + mc.fontRenderer
                    .getStringWidth(LINES.get(firstHighlighting[0]).substring(0, firstHighlighting[1])) - 1;
            int h_miny = yoffset + ((CHARHEIGHT - 1) * lineAtOnScreen_i) - 2;
            int h_maxx = h_minx + mc.fontRenderer
                    .getStringWidth(LINES.get(firstHighlighting[0]).substring(firstHighlighting[1])) + 2;
            int h_maxy = h_miny + CHARHEIGHT;

            if (lastHighlighting[0] != firstHighlighting[0]) {
                if (lastHighlighting[0] < firstHighlighting[0]) {
                    h_maxx = xoffset - 1;
                }

                drawRect(h_minx, h_miny, h_maxx, h_maxy, ConfigHandler.COLOR_TEXT_HIGHLIGHT);

                //inbetween
                int firstOnScreen = lineAtOnScreen_i <= lineAtOnScreen_f ? lineAtOnScreen_i : lineAtOnScreen_f;
                int firstInLINES = firstHighlighting[0] <= lastHighlighting[0] ? firstHighlighting[0]
                        : lastHighlighting[0];

                for (int i = 1; i < Math.abs(lineAtOnScreen_i - lineAtOnScreen_f); i++) {
                    h_minx = xoffset - 1;
                    h_miny = yoffset + ((CHARHEIGHT - 1) * (firstOnScreen + i)) - 2;
                    h_maxx = h_minx + mc.fontRenderer.getStringWidth(LINES.get(firstInLINES + i)) + 2;
                    h_maxy = h_miny + CHARHEIGHT;

                    drawRect(h_minx, h_miny, h_maxx, h_maxy, ConfigHandler.COLOR_TEXT_HIGHLIGHT);
                }

                //final
                h_minx = xoffset
                        + mc.fontRenderer
                                .getStringWidth(LINES.get(lastHighlighting[0]).substring(0, lastHighlighting[1]))
                        - 1;
                h_miny = yoffset + ((CHARHEIGHT - 1) * lineAtOnScreen_f) - 2;
                h_maxx = h_minx + mc.fontRenderer
                        .getStringWidth(LINES.get(lastHighlighting[0]).substring(lastHighlighting[1])) + 2;
                h_maxy = h_miny + CHARHEIGHT;

                if (lastHighlighting[0] > firstHighlighting[0]) {
                    h_maxx = xoffset - 1;
                }

                drawRect(h_minx, h_miny, h_maxx, h_maxy, ConfigHandler.COLOR_TEXT_HIGHLIGHT);
            } else {
                h_maxx = xoffset
                        + mc.fontRenderer
                                .getStringWidth(LINES.get(lastHighlighting[0]).substring(0, lastHighlighting[1]))
                        - 1;
                drawRect(h_minx, h_miny, h_maxx, h_maxy, ConfigHandler.COLOR_TEXT_HIGHLIGHT);
            }
        }

        // Past messages - text
        int max = (message_maxy - message_miny) / (CHARHEIGHT - 1);
        if (slider != 0) {
            slider = LINES.size() - slider > max ? (LINES.size() - slider < LINES.size() ? slider : 0)
                    : LINES.size() - max;
        }

        if (slider < 0)
            slider = 0;

        int oversize = 0;
        for (int i = 0; i + oversize < LINES.size() && i + oversize < max; i++) {
            int element = LINES.size() - 1 - i - slider;
            if (LINES.size() <= element)
                continue;
            drawString(this.mc.fontRenderer, LINES.elementAt(element),
                    textbox_minx + ConfigHandler.SCREEN_BORDERSIZE, textbox_miny - CHARHEIGHT + 1
                            - ConfigHandler.SCREEN_BORDERSIZE - ((i + oversize) * (CHARHEIGHT - 1)),
                    ConfigHandler.COLOR_TEXT_OUTPUT);
        }

        // Scroll - background
        int scroll_minx = textbox_minx + currentChatWidth + ConfigHandler.SCREEN_BORDERSIZE;
        int scroll_maxx = textbox_maxx;
        int scroll_miny = message_miny;
        int scroll_maxy = textbox_miny - ConfigHandler.SCREEN_BORDERSIZE;
        drawRect(scroll_minx, scroll_miny, scroll_maxx, scroll_maxy, ConfigHandler.COLOR_SCROLL_BACKGROUND);

        // Scroll - button top
        TOP = new int[] { scroll_minx + 1, scroll_miny + 1, scroll_maxx - 1, scroll_miny + 9 };
        drawRect(TOP[0], TOP[1], TOP[2], TOP[3], ConfigHandler.COLOR_SCROLL_FOREGROUND);
        drawString(this.mc.fontRenderer, "^", TOP[0] + 2, TOP[1] + 2, ConfigHandler.COLOR_SCROLL_ARROW);

        // Scroll - button bottom
        BOTTOM = new int[] { scroll_minx + 1, scroll_maxy - 9, scroll_maxx - 1, scroll_maxy - 1 };
        drawRect(BOTTOM[0], BOTTOM[1], BOTTOM[2], BOTTOM[3], ConfigHandler.COLOR_SCROLL_FOREGROUND);
        drawStringFlipped(this.mc.fontRenderer, "^", BOTTOM[0] + 1, BOTTOM[1] - 3, ConfigHandler.COLOR_SCROLL_ARROW,
                true);

        // Scroll - bar
        int scrollable_minx = scroll_minx + 1;
        int scrollable_maxx = scroll_maxx - 1;
        int scrollable_miny = scroll_miny + 11;
        int scrollable_maxy = scroll_maxy - 10;
        sliderHeight = scrollable_maxy - scrollable_miny;
        double heightpercentage = (double) max / (double) LINES.size();
        double barheight = (sliderHeight) * heightpercentage;
        barheight = (barheight < 5) ? 5 : barheight;
        double stepsize = (sliderHeight - barheight) / (double) (LINES.size() - max);
        double position = slider * stepsize;

        if (LINES.size() < max)
            BAR = new int[] { scrollable_minx, scrollable_miny, scrollable_maxx, scrollable_maxy };
        else
            BAR = new int[] { scrollable_minx, (int) (scrollable_maxy - position - barheight), scrollable_maxx,
                    (int) (scrollable_maxy - position) };
        drawRect(BAR[0], BAR[1], BAR[2], BAR[3], ConfigHandler.COLOR_SCROLL_FOREGROUND);

        // Input
        validateCursor();
        validateOffset();
        drawString(this.mc.fontRenderer, input, textbox_minx + ConfigHandler.SCREEN_BORDERSIZE, textbox_miny + 1,
                ConfigHandler.COLOR_INPUT_TEXT);

        //autocomplete wordmatch visual
        int linesToShow = (int) Math.floor(ConfigHandler.SCREEN_PADDING_BOTTOM / CHARHEIGHT) - 1;

        if (tabbing && ConfigHandler.SCREEN_AUTOPREVIEW && linesToShow > 0) {
            int tabStartPos = mc.fontRenderer
                    .getStringWidth(ConfigHandler.CHAT_INPUT_PREFIX + " " + message.substring(0, tabWordPos));
            if (tabStartPos + ConfigHandler.SCREEN_AUTOPREVIEWAREA > width)
                tabStartPos = width - ConfigHandler.SCREEN_AUTOPREVIEWAREA;

            drawRect(textbox_minx - ConfigHandler.SCREEN_BORDERSIZE * 2 + tabStartPos,
                    textbox_maxy + ConfigHandler.SCREEN_BORDERSIZE,
                    tabStartPos + ConfigHandler.SCREEN_AUTOPREVIEWAREA, height, ConfigHandler.COLOR_BASE);

            String currentPos = String.valueOf(tabListPos + 1);
            String endPos = String.valueOf(tabMaxPos);
            int charDiff = endPos.length() - currentPos.length();
            for (int i = 0; i < charDiff; i++) {
                currentPos = "0" + currentPos;
            }

            String positionText = "[" + currentPos + "/" + endPos + "]";

            for (int i = 0; i < linesToShow; i++) {
                drawString(this.mc.fontRenderer, tabCurrentList.get((tabListPos + i + 1) % (tabMaxPos)),
                        textbox_minx + tabStartPos - ConfigHandler.SCREEN_BORDERSIZE,
                        textbox_maxy + ConfigHandler.SCREEN_BORDERSIZE + i * CHARHEIGHT,
                        ConfigHandler.COLOR_INPUT_TEXT);
            }

            drawString(this.mc.fontRenderer, positionText,
                    textbox_minx + tabStartPos - ConfigHandler.SCREEN_BORDERSIZE,
                    textbox_maxy + ConfigHandler.SCREEN_BORDERSIZE + linesToShow * CHARHEIGHT,
                    ConfigHandler.COLOR_INPUT_TEXT);
        }

        // Titlebar
        drawRect(maxx / 2, 0, maxx, miny, ConfigHandler.COLOR_BASE);

        // Title
        drawString(this.mc.fontRenderer, MCConsole.MODNAME, (maxx / 2) + ConfigHandler.SCREEN_BORDERSIZE,
                ConfigHandler.SCREEN_BORDERSIZE, ConfigHandler.COLOR_TEXT_TITLE);

        // Options button 
        //if(MCConsole.GuiApiInstalled()) {
        OPTION_BUTTON = new int[] { maxx - ConfigHandler.SCREEN_BORDERSIZE * 3 - 30,
                ConfigHandler.SCREEN_BORDERSIZE, maxx - ConfigHandler.SCREEN_BORDERSIZE * 3 - 20, miny };
        drawRect(OPTION_BUTTON[0], OPTION_BUTTON[1], OPTION_BUTTON[2], OPTION_BUTTON[3],
                ConfigHandler.COLOR_EXIT_BUTTON);
        drawString(this.mc.fontRenderer, "|:.", maxx - ConfigHandler.SCREEN_BORDERSIZE * 3 - 28,
                ConfigHandler.SCREEN_BORDERSIZE + 2, ConfigHandler.COLOR_EXIT_BUTTON_TEXT);
        //}

        // External window button 
        EXTERNAL_BUTTON = new int[] { maxx - ConfigHandler.SCREEN_BORDERSIZE * 2 - 20,
                ConfigHandler.SCREEN_BORDERSIZE, maxx - ConfigHandler.SCREEN_BORDERSIZE * 2 - 10, miny };
        drawRect(EXTERNAL_BUTTON[0], EXTERNAL_BUTTON[1], EXTERNAL_BUTTON[2], EXTERNAL_BUTTON[3],
                ConfigHandler.COLOR_EXIT_BUTTON);
        drawString(this.mc.fontRenderer, "[]", maxx - ConfigHandler.SCREEN_BORDERSIZE * 2 - 19,
                ConfigHandler.SCREEN_BORDERSIZE + 2, ConfigHandler.COLOR_EXIT_BUTTON_TEXT);

        // Exit button
        EXIT_BUTTON = new int[] { maxx - ConfigHandler.SCREEN_BORDERSIZE - 10, ConfigHandler.SCREEN_BORDERSIZE,
                maxx - ConfigHandler.SCREEN_BORDERSIZE, miny };
        drawRect(EXIT_BUTTON[0], EXIT_BUTTON[1], EXIT_BUTTON[2], EXIT_BUTTON[3], ConfigHandler.COLOR_EXIT_BUTTON);
        drawString(this.mc.fontRenderer, "X", maxx - ConfigHandler.SCREEN_BORDERSIZE - 7,
                ConfigHandler.SCREEN_BORDERSIZE + 2, ConfigHandler.COLOR_EXIT_BUTTON_TEXT);

        super.drawScreen(mousex, mousey, f);
    }

    /**
     * Draws the specified String flipped upside down
     *
     * @param fontrenderer
     * @param s string to draw
     * @param i position of the x coordinate
     * @param j position of the y coordinate
     * @param k colour of the render
     * @param flag if true draw with shadow, if false draw without shadow
     */

    public void drawStringFlipped(FontRenderer fontrenderer, String s, int i, int j, int k, boolean flag) {
        GL11.glPushMatrix();
        GL11.glScalef(-1F, -1F, 1F);
        GL11.glTranslatef((-i * 2) - fontrenderer.getStringWidth(s), (-j * 2) - fontrenderer.FONT_HEIGHT, 0.0F);
        if (flag) {
            fontrenderer.drawString(s, i - 1, j - 1, (k & 0xfcfcfc) >> 2 | k & 0xff000000); //Took the last argument from FrontRenderer.renderString() because it's private and I want the shadow on the correct side when flipped
        }
        fontrenderer.drawString(s, i, j, k);
        GL11.glPopMatrix();
    }

    /**
     * Tests whether the (x, y) coordinate is within the rectangle or not
     *
     * @param x x coordinate
     * @param y y coordinate
     * @param rect integer array in the form of {mix x, min y, max x, max y}
     * @return true if point is in rect false if point is not in rect
     */

    public boolean hitTest(int x, int y, int[] rect) {
        if (x >= rect[0] && x <= rect[2] && y >= rect[1] && y <= rect[3])
            return true;
        else
            return false;
    }

    /**
     * Returns the mouse position as an index within the string line
     *
     * @param x The mouse's x position relative to the start of the string line
     * @param line The string to find the index in
     * @return the character index the mouse clicked at.
     */

    public int mouseAt(int x, String line) {
        int left = 0;
        int right = line.length();

        if (x >= mc.fontRenderer.getStringWidth(line)) {
            return line.length();
        }

        while (left <= right) {
            int middle = (left + right) / 2;
            int length = mc.fontRenderer.getStringWidth(line.substring(0, middle));
            double upper, lower;
            if (middle < line.length() - 1) {
                upper = length + (mc.fontRenderer.getStringWidth(Character.toString(line.charAt(middle))) / 2.0);
            } else {
                upper = mc.fontRenderer.getStringWidth(line);
            }

            if (middle >= 1) {
                lower = length
                        - (mc.fontRenderer.getStringWidth(Character.toString(line.charAt(middle - 1))) / 2.0);
            } else {
                lower = 0;
            }

            if ((x <= upper && x >= lower)) {
                return middle;
            } else if (x < lower) {
                right = middle - 1;
            } else if (x > upper) {
                left = middle + 1;
            }
        }

        return 0;
    }

    /**
     * Called on mouse clicked and processes the button clicks and actions
     *
     * @see net.minecraft.src.GuiScreen#mouseClicked(int, int, int)
     */
    @Override
    protected void mouseClicked(int mousex, int mousey, int button) {
        if (button == 0) {
            // Bad implementation which checks for clicks on exit button
            if (hitTest(mousex, mousey, EXIT_BUTTON)) {
                mc.displayGuiScreen(null);
                resetTabbing();
                return;
            } else if (hitTest(mousex, mousey, EXTERNAL_BUTTON)) {
                ExternalGuiConsole.toggleExternalWIndow();
                //} else if (mod_Console.GuiApiInstalled() && hitTest(mousex, mousey, OPTION_BUTTON)){
            } else if (hitTest(mousex, mousey, OPTION_BUTTON)) {
                //mc.displayGuiScreen(null);
                //GuiModScreen.show(new GuiModScreen(INSTANCE, ConsoleSettings.getMainWindow()));
                mc.displayGuiScreen(new ConfigGui(new GuiIngameModOptions(mc.currentScreen)));
                //mc.displayGuiScreen(ConfigGuiFactory.mainConfigGuiClass());
                resetTabbing();
                return;
                // Bad implementation which checks for clicks on scrollbar
            } else if (hitTest(mousex, mousey, TOP))
                slider++;
            else if (hitTest(mousex, mousey, BOTTOM))
                slider--;
            else if (hitTest(mousex, mousey, BAR)) {
                isSliding = true;
                lastSliding = mousey;
                initialSliding = slider;
            } else if (hitTest(mousex, mousey, TEXT_BOX)) {
                resetTabbing();
                isHighlighting = true;
                firstHighlighting[0] = lastHighlighting[0] = -1;
                int mousexCorrected = mousex - TEXT_BOX[0] - ConfigHandler.SCREEN_BORDERSIZE;
                int startStringIndex = 0;
                int cutPrefixChars = (inputOffset <= ConfigHandler.CHAT_INPUT_PREFIX.length() ? inputOffset
                        : ConfigHandler.CHAT_INPUT_PREFIX.length());
                if (inputOffset < ConfigHandler.CHAT_INPUT_PREFIX.length()) {
                    mousexCorrected -= mc.fontRenderer
                            .getStringWidth(ConfigHandler.CHAT_INPUT_PREFIX.substring(inputOffset));
                } else {
                    startStringIndex = inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length();
                }

                if (mousexCorrected > mc.fontRenderer
                        .getStringWidth(message.substring(startStringIndex, cursor) + "!")) {
                    mousexCorrected -= mc.fontRenderer.getStringWidth("!");
                }

                int charat = mouseAt(mousexCorrected, message.substring(startStringIndex)) + startStringIndex;
                if (message.length() < charat)
                    firstHighlighting[1] = message.length();
                else
                    firstHighlighting[1] = charat;
                cursor = lastHighlighting[1] = firstHighlighting[1];
            } else if (hitTest(mousex, mousey, HISTORY)) {
                resetTabbing();
                isHighlighting = true;
                int mousexCorrected = mousex - HISTORY[0] - ConfigHandler.SCREEN_BORDERSIZE;
                int lineAt = correctYlineAt(mousey);
                firstHighlighting[0] = lineAt;
                int charAt = mouseAt(mousexCorrected, LINES.get(lineAt));
                firstHighlighting[1] = charAt;
            }

            super.mouseClicked(mousex, mousey, button);
        }
    }

    /**
     * Method is called on mouse movement and used to determine slider movement
     *
     * @see net.minecraft.src.GuiScreen#mouseMovedOrUp(int, int, int)
     */
    @Override
    protected void mouseMovedOrUp(int mousex, int mousey, int button) {
        int wheel = Mouse.getDWheel();
        if (wheel != 0) {
            slider += wheel / 120 * ConfigHandler.SCREEN_LINES_PER_SCROLL;
        }

        // Moves the slider position
        if (isSliding) {
            if (Mouse.isButtonDown(0)) {
                int diff = lastSliding - mousey;
                if (diff != 0) {
                    slider = initialSliding + (int) ((diff / (double) sliderHeight) * (LINES.size()));
                }
            } else {
                isSliding = false;
                lastSliding = 0;
                initialSliding = 0;
            }
        } else if (isHighlighting) {
            if (hitTest(mousex, mousey, TEXT_BOX) || firstHighlighting[0] == -1) {
                int mousexCorrected = mousex - TEXT_BOX[0] - ConfigHandler.SCREEN_BORDERSIZE;
                int startStringIndex = 0;

                if (inputOffset < ConfigHandler.CHAT_INPUT_PREFIX.length()) {
                    mousexCorrected -= mc.fontRenderer
                            .getStringWidth(ConfigHandler.CHAT_INPUT_PREFIX.substring(inputOffset));
                } else {
                    startStringIndex = inputOffset - ConfigHandler.CHAT_INPUT_PREFIX.length();
                }

                if (mousexCorrected > mc.fontRenderer
                        .getStringWidth(message.substring(startStringIndex, cursor) + "!")) {
                    mousexCorrected -= mc.fontRenderer.getStringWidth("!");
                }

                int charat = mouseAt(mousexCorrected, message.substring(startStringIndex)) + startStringIndex;
                if (charat < 0) {
                    charat = 0;
                }
                if (message.length() < charat) {
                    lastHighlighting[1] = message.length();
                } else {
                    lastHighlighting[1] = charat;
                }
                if (firstHighlighting[0] == lastHighlighting[0] && firstHighlighting[0] == -1) {
                    cursor = lastHighlighting[1];
                    validateCursor();
                }
                validateOffset();
            } else if (hitTest(mousex, mousey, HISTORY)) {
                resetTabbing();
                isHighlighting = true;
                int mousexCorrected = mousex - HISTORY[0] - ConfigHandler.SCREEN_BORDERSIZE;
                int lineAt = correctYlineAt(mousey);
                lastHighlighting[0] = lineAt;
                lineAt = (lineAt >= LINES.size()) ? LINES.size() - 1 : lineAt;
                int charAt = mouseAt(mousexCorrected, LINES.get(lineAt));
                lastHighlighting[1] = charAt;
            }
            if (!Mouse.isButtonDown(0)) {
                isHighlighting = false;
            }
        }
    }

    private int correctYlineAt(int mousey) {
        int maxDisplayedLines = (HISTORY[3] - HISTORY[1]) / (CHARHEIGHT - 1);
        int linesDisplayed = LINES.size() >= maxDisplayedLines ? maxDisplayedLines : LINES.size();
        int mouseyCorrected = mousey - HISTORY[1] - ConfigHandler.SCREEN_BORDERSIZE;
        int lineAt = mouseyCorrected / (CHARHEIGHT - 1) + LINES.size() - linesDisplayed - slider;

        if (lineAt >= LINES.size()) {
            lineAt = LINES.size() - maxDisplayedLines + lineAt;
            if (lineAt < 0) {
                lineAt = 0;
            } else if (lineAt >= LINES.size()) {
                lineAt = LINES.size() - 1;
            }
        }

        return lineAt;
    }

    /**
     * Returns true if the GUI is open
     *
     * @return Returns whether the GUI is open or not
     */
    public boolean isGuiOpen() {
        return isGuiOpen;
    }

    /**
     * Adds a console listener to the console. When events are triggered they
     * are then sent to all the listeners in the order which they are registered
     * in
     *
     * @param cl - The listener to add
     */
    public void addConsoleListener(ConsoleListener cl) {
        if (!LISTENERS.contains(cl)) {
            LISTENERS.add(cl);
        }
    }

    /**
     * Adds an input message to the console
     *
     * @param message - The input message
     */
    private void addInputMessage(String innMessage) {
        String message = innMessage;

        if (ConfigHandler.CHAT_PRINT_INPUT) {
            MESSAGES.add(ConfigHandler.CHAT_INPUT_PREFIX + message);
            addLine(ConfigHandler.CHAT_INPUT_PREFIX + message);
        }

        INPUT_HISTORY.add(message);
        if ((ConfigHandler.LOGGING & ConfigHandler.LOGGING_INPUT) > 0) {
            log.add(ConfigHandler.CHAT_INPUT_PREFIX + message);
        }

        boolean post = true;
        for (ConsoleListener cl : LISTENERS) {
            if (!cl.processInput(message)) {
                post = false;
            }
        }

        if (post) {
            int lastLen = 0;
            for (int i = 0; i <= message.length() / ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX; i++) {
                int end = (lastLen + ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX) > message.length()
                        ? message.length()
                        : (lastLen + ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX);
                if (message.length() > ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX
                        && message.substring(lastLen, end).length() >= ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX) {
                    for (int j = 1; j <= 10; j++) {
                        if (message.charAt(end - j) == ' ') { //Wrap at space if it's within 10 characters
                            end = end - j;
                            break;
                        }
                    }
                }

                mc.thePlayer.sendChatMessage(message.substring(lastLen, end));

                if (message.length() == ConfigHandler.CHAT_INPUT_LENGTH_SERVER_MAX) {
                    break; //Fix for displaying an extra line when length is exactly at the limit
                }
                lastLen = end;
            }
        }

        if (ConfigHandler.CHAT_UNPASUE_PAUSE_WITH_MESSAGE && isGuiOpen) {
            pauseGame = false;
            pauseCountDown = 2;
        }
    }

    /**
     * Handles unclean messages from other
     * 
     * @param message
     */
    public void sendUncleanMessage(String message) {
        String cleanMessage = cleanString(message);
        if (!cleanMessage.isEmpty() && ConfigHandler.VALID_MESSAGE.matcher(cleanMessage).find()) {
            addInputMessage(cleanMessage);
        }
    }

    /**
     * Handles messages received as client 
     * 
     * @param message - the message
     */
    public void addClientMessage(String message) {
        addOutputMessage(message);
    }

    /**
     * Handles message sent as server
     * 
     * @param handler - who sent the message
     * @param message - the message
     */
    public void addServerMessage(ServerChatEvent event) {
        if (!event.username.equals(playername))
            addOutputMessage(ConfigHandler.CHAT_INPUT_PREFIX + event.message);
    }

    /**
     * Adds an output message to the console
     *
     * @param message - The output message
     */
    public void addOutputMessage(String message) {
        if (ConfigHandler.CHAT_PRINT_OUTPUT) {
            MESSAGES.add(message);
            addLine(message);
        }
        if ((ConfigHandler.LOGGING & ConfigHandler.LOGGING_OUTPUT) > 0) {
            log.add(message);
        }

        for (ConsoleListener cl : LISTENERS) {
            cl.processOutput(message);
        }

    }

    /**
     * Not anymore, new default at 100ms.
     * (Runs a thread which automatically pulls the chat line into the console
     * on a configurable interval, by default 20ms. It uses Object.equals
     * against the ChatLine object to determine the previous message which was
     * copied across.)
     *
     * This method also clears the history and output lists once they reach
     * capacity
     *
     * This method also handles key bindings
     *
     * @see java.lang.Runnable#run()
     */
    @Override
    public void run() {
        while (true) {
            try {
                if (logName == null) {
                    logName = (new SimpleDateFormat(ConfigHandler.DATE_FORMAT_FILENAME)).format(new Date());
                }
                if (lastWrite + ConfigHandler.LOG_WRITE_INTERVAL < System.currentTimeMillis()) {
                    try {
                        FileOutputStream fos = new FileOutputStream(new File(ConfigHandler.LOG_DIR, logName), true);
                        while (log.size() > 0) {
                            String line = sdf.format(new Date()) + log.elementAt(0) + ConfigHandler.LINE_BREAK;
                            fos.write(line.getBytes());
                            log.remove(0);
                        }
                        fos.close();
                        lastWrite = System.currentTimeMillis();
                    } catch (FileNotFoundException e) {
                    }
                }

                // Empties message list when it hits maximum size
                while (ConfigHandler.CHAT_OUTPUT_MAX != 0 && MESSAGES.size() > ConfigHandler.CHAT_OUTPUT_MAX) {
                    MESSAGES.remove(0);
                    rebuildLines = true;
                }

                // Empties input history list when it hits maximum size
                while (INPUT_HISTORY.size() > ConfigHandler.CHAT_INPUT_HISTORY_MAX) {
                    INPUT_HISTORY.remove(0);
                }

                // Verify key down list items are still down - repeat event workaround
                for (int i = 0; i < keyDown.size(); i++) {
                    if (!Keyboard.isKeyDown(keyDown.get(i))) {
                        keyDown.remove(keyDown.get(i));
                    }
                }

                // Key bindings
                // String <code,code,code,...> : value > HashMap<String(codes),String(value)>
                Iterator<String> i = keyBindings.keySet().iterator();
                while (i.hasNext()) {
                    String k = i.next();
                    if (k == null) {
                        continue;
                    }
                    String keys[] = k.split(",");
                    try {
                        boolean execute = true;
                        int keydown = 0;
                        for (String key : keys) {
                            int keyvalue = Integer.parseInt(key);
                            if (!Keyboard.isKeyDown(keyvalue)) {
                                execute = false;
                                break;
                            }
                            if (keyDown.contains(keyvalue)) {
                                keydown++;
                            }
                            keyDown.add(keyvalue);
                        }
                        if (execute && keydown < keys.length) {
                            if (BACKGROUND_BINDING_EVENTS || mc.currentScreen == null) {
                                // Adds binding message to input: addInputMessage(keyBindings.get(k));
                                mc.thePlayer.sendChatMessage(keyBindings.get(k));
                            }
                        }
                    } catch (Exception e) { // If the number can't parse it is invalid anyway
                    }
                }

                Thread.sleep(ConfigHandler.POLL_DELAY);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Reads the settings from the specified file and sets them to their
     * applicable variables
     *
     * @param base - The class to set the settings in
     * @param settings - The settings file to load the values from
     */
    public static void readSettings(Class<?> base, File settings) {
        Properties p = new Properties();
        try {
            File CanonicalFile = settings.getCanonicalFile();
            p.load(new FileInputStream(CanonicalFile));
            Field[] declaredFields = base.getDeclaredFields();
            for (Field field : declaredFields) {
                if (Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
                    try {
                        if (!field.isAccessible()) {
                            field.setAccessible(true);
                        }

                        if (field.getType().equals(String.class)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, property);
                            }
                        } else if (field.getType().equals(Integer.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Integer.decode(property).intValue());
                            }
                        } else if (field.getType().equals(Double.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Double.parseDouble(property));
                            }
                        } else if (field.getType().equals(Boolean.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Boolean.parseBoolean(property));
                            }
                        } else if (field.getType().equals(Long.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Long.decode(property).longValue());
                            }
                        } else if (field.getType().equals(Byte.TYPE)) { // new
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Byte.parseByte(property));
                            }
                        } else if (field.getType().equals(Float.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Float.parseFloat(property));
                            }
                        } else if (field.getType().equals(Short.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, Short.decode(property));
                            }
                        } else if (field.getType().equals(Character.TYPE)) {
                            String property = (String) p.get(field.getName());
                            if (property != null) {
                                field.set(null, property.charAt(0));
                            }
                        }
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Writes the variables from the specified class into the specified file
     * in a .properties format
     *
     * @param base - The class to get the settings from
     * @param settings - The settings file to save the values to
     */
    public static void writeSettings(Class<?> base, File settings) {
        Properties p = new Properties();
        try {
            File CanonicalFile = settings.getCanonicalFile();
            try {
                p.load(new FileInputStream(CanonicalFile));
            } catch (Exception e) {
            }
            Field[] declaredFields = base.getDeclaredFields();
            for (Field field : declaredFields) {
                if (Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
                    try {
                        if (!field.isAccessible()) {
                            field.setAccessible(true);
                        }
                        if (field.getType().equals(Integer.TYPE)) {
                            p.setProperty(field.getName(), field.getInt(null) + "");
                        } else if (field.getType().equals(Double.TYPE)) {
                            p.setProperty(field.getName(), field.getDouble(null) + "");
                        } else if (field.getType().equals(Boolean.TYPE)) {
                            p.setProperty(field.getName(), field.getBoolean(null) + "");
                        } else if (field.getType().equals(Long.TYPE)) {
                            p.setProperty(field.getName(), field.getLong(null) + "");
                        } else if (field.getType().equals(String.class)) {
                            p.setProperty(field.getName(), (String) field.get(null));
                        } else if (field.getType().equals(Byte.TYPE)) {
                            p.setProperty(field.getName(), field.getByte(null) + "");
                        } else if (field.getType().equals(Short.TYPE)) {
                            p.setProperty(field.getName(), field.getShort(null) + "");
                        } else if (field.getType().equals(Float.TYPE)) {
                            p.setProperty(field.getName(), field.getFloat(null) + "");
                        } else if (field.getType().equals(Character.TYPE)) {
                            p.setProperty(field.getName(), field.getChar(null) + "");
                        }
                    } catch (Exception e) {
                    }
                }
            }
            p.store(new FileOutputStream(CanonicalFile), "");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Reads the static non final fields and return all of type
     * String, Double, Boolean, Long, Byte, Float, Short, Character
     *
     * @param base - The class to get the fields from
     * @return ArrayList<Field> containing all option fields
     */
    public static ArrayList<Field> returnSettingsFields(Class<?> base) {
        ArrayList<Field> fields = new ArrayList<Field>();
        try {
            Field[] declaredFields = base.getDeclaredFields();
            for (Field field : declaredFields) {
                if (Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
                    try {
                        if (!field.isAccessible()) {
                            field.setAccessible(true);
                        }

                        if (field.getType().equals(String.class) || field.getType().equals(Integer.TYPE)
                                || field.getType().equals(Double.TYPE) || field.getType().equals(Boolean.TYPE)
                                || field.getType().equals(Long.TYPE) || field.getType().equals(Byte.TYPE)
                                || field.getType().equals(Float.TYPE) || field.getType().equals(Short.TYPE)
                                || field.getType().equals(Character.TYPE)) {
                            fields.add(field);
                        }
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fields;
    }
}