package client.gui;

import java.util.List;

import client.Client;
import client.gui.element.ActButton;
import client.gui.element.Area;
import client.gui.element.ButtonCallback;
import client.gui.element.Element;
import client.gui.element.Fill;
import client.gui.element.PressType;
import client.renderer.Drawing;
import client.gui.element.FieldAction;
import client.gui.element.Field;
import client.gui.element.FieldCallback;
import client.vars.BoolVar;
import client.vars.CVar;
import client.window.Keysym;
import common.collect.Lists;
import common.network.IPlayer;
import common.packet.CPacketComplete;
import common.util.BlockPos;
import common.util.ExtMath;
import common.util.HitPosition;
import common.util.Color;

public class GuiConsole extends Gui implements FieldCallback {
	private class ConsoleArea extends Area {
		private final boolean background;
		
		public ConsoleArea(int x, int y, int w, int h, String text, boolean background) {
			super(x, y, w, h, text);
			this.background = background;
		}

		protected void drawBackground() {
			if(this.background)
				Drawing.drawRect(this.pos_x, this.pos_y, this.size_x, this.size_y, 0x3f000000);
		}
		
		protected void onDoubleClick() {
			if(this.sel_start >= 0 && this.sel_end == this.sel_start) {
				for(int z = this.sel_start - 1; z >= 0; z--) {
					char c = this.text.charAt(z);
					if(c == Color.COMMAND) {
						for(int n = z + 1; n < this.text.length(); n++) {
							c = this.text.charAt(n);
							if(c < 0x20) {
								GuiConsole.this.send("/" + this.text.substring(z + 1, n), false);
								return;
							}
						}
						break;
					}
					else if(c < 0x20) {
						break;
					}
				}
			}
			super.onDoubleClick();
		}
	}
	
	public static final GuiConsole INSTANCE = new GuiConsole();

	private final List<String> sentMessages = Lists.<String>newArrayList();

    private boolean full;
    
    private String historyBuffer = "";
    private int sentHistoryCursor = -1;
    private boolean playerNamesFound;
    private boolean waitingOnAutocomplete;
    private boolean reverse;
    private String prefixFirst;
    private int autocompleteIndex;
    private List<String> foundPlayerNames = Lists.<String>newArrayList();
    private Field inputField;
	private ConsoleArea logBox;
	
	public GuiConsole setFull(boolean full) {
		this.full = full;
		return this;
	}
	
	public void init(int width, int height) {
		if(this.full) {
			this.addSelector("con_autoclose", 0, 0, 160, 0);
			this.addSelector("con_timestamps", 160, 0, 160, 0);
			this.addSelector("con_loglevel", 320, 0, 160, 0);
			this.add(new ActButton(480, 0, 160, 0, new ButtonCallback() {
				public void use(ActButton elem, PressType action) {
					GuiConsole.this.reset();
					GuiConsole.this.setLog(GuiConsole.this.gm.getBuffer());
				}
			}, "Löschen"));
		}
		this.logBox = this.add(new ConsoleArea(0, this.full ? Element.BASE_HEIGHT : 0, width, height - Element.BASE_HEIGHT * (this.full ? 2 : 1), this.gm.getBuffer(), this.gm.world != null && !this.gm.charEditor));
		if(this.full)
			this.add(new Fill(640, 0, width - 640, 0, ""));
		this.inputField = this.add(new Field(0, height - Element.BASE_HEIGHT, width, 0, IPlayer.MAX_CMD_LENGTH, this, ""));
		this.inputField.setSelected();
        this.sentHistoryCursor = this.sentMessages.size();
	}
	
	public String getTitle() {
		return "Konsole / Chat";
	}
	
	public void reset() {
		this.gm.reset();
		this.sentMessages.clear();
		this.sentHistoryCursor = -1;
	}
	
	public void setLog(String buffer) {
		if(this.logBox != null)
			this.logBox.setText(buffer);
	}
	
	public void drawMainBackground() {
		if(this.gm.world == null || this.gm.charEditor)
			super.drawMainBackground();
	}

	public void key(Keysym key, boolean ctrl, boolean shift) {
		super.key(key, ctrl, shift);
//        this.waitingOnAutocomplete = false;
		if(key != Keysym.TAB && key != Keysym.LEFT_SHIFT && key != Keysym.RIGHT_SHIFT)
			this.playerNamesFound = false;
	}

    public void use(Field elem, FieldAction value)
    {
        this.waitingOnAutocomplete = false;

        if (value == FieldAction.FORWARD || value == FieldAction.BACKWARD)
        {
        	this.reverse = value == FieldAction.BACKWARD;
            this.autocompletePlayerNames();
        }
        else
        {
            this.playerNamesFound = false;
        }

        if(value == FieldAction.PREVIOUS)
                this.getSentHistory(-1);
        else if (value == FieldAction.NEXT)
            this.getSentHistory(1);
        if(value == FieldAction.SEND)
        {
            String s = this.inputField.getText().trim();

            this.send(s, true);
        }
    }

	protected void send(String text, boolean clear) {
		if(!text.isEmpty()) {
			if(this.sentMessages.isEmpty() || !((String)this.sentMessages.get(this.sentMessages.size() - 1)).equals(text))
				this.sentMessages.add(text);
			this.sentHistoryCursor = this.sentMessages.size();
			this.gm.exec(text);
		}

		if(clear)
			this.inputField.setText("");
		if((this.gm.conAutoclose || !this.full) && this.gm.world != null)
			this.gm.show(null);
	}

    protected void setText(String newChatText, boolean shouldOverwrite)
    {
        if (shouldOverwrite)
        {
            this.inputField.setText(newChatText);
        }
        else
        {
            this.inputField.insertText(newChatText);
        }
    }
		
	private void addMessage(String msg) {
		String buffer = this.gm.getBuffer();
		if((buffer.length() + msg.length() + 2) > Client.LOG_BUFFER) {
			int offset = (msg.length() + 2) > 1024 ? (msg.length() + 2) : 1024;
			int nl = buffer.indexOf('\n', offset);
			buffer = nl >= 0 ? buffer.substring(nl + 1) : "";
		}
		this.setLog(buffer + "\n" + Color.RESET + msg);
	}

    public void autocompletePlayerNames()
    {
        if (this.playerNamesFound)
        {
            this.inputField.deleteSpaceToCur();

            if (this.autocompleteIndex >= this.foundPlayerNames.size())
            {
                this.autocompleteIndex = 0;
            }
            else if (this.autocompleteIndex < 0)
            {
                this.autocompleteIndex = this.foundPlayerNames.size() - 1;
            }
        }
        else
        {
            int i = this.inputField.getSpaceBeforeCur();
            this.foundPlayerNames.clear();
            this.autocompleteIndex = 0;
//            String s = this.inputField.getText().substring(i).toLowerCase();
            String s1 = this.inputField.getText().substring(0, this.inputField.getCursorPos());
            String[] localMatches = this.sendAutocompleteRequest(s1);
            if(localMatches != null) {
            	this.onAutocompleteResponse(localMatches);
            	return;
            }

            if (this.foundPlayerNames.isEmpty())
            {
                return;
            }

            this.playerNamesFound = true;
            this.inputField.deleteSpaceToCur();
        }

        if (this.foundPlayerNames.size() > 1)
        {
            StringBuilder stringbuilder = new StringBuilder();

            for (String s2 : this.foundPlayerNames)
            {
                if (stringbuilder.length() > 0)
                {
                    stringbuilder.append(", ");
                }

                stringbuilder.append(s2);
            }

            this.addMessage(stringbuilder.toString());
        }

        this.inputField.insertText((String)this.foundPlayerNames.get(this.autocompleteIndex));
        this.autocompleteIndex += this.reverse ? -1 : 1;
    }
    
    private String[] complete(String s) {
    	List<String> list = Lists.<String>newArrayList();
    	String[] argv = s.split(" ", -1);
    	String pre = argv[0];
    	s = argv[argv.length - 1];
    	Iterable<String> res = pre.startsWith("#") ? 
      		(argv.length == 1 ? this.gm.getVars() : (argv.length == 2 ? getVarCompletion(argv[0].substring(1)) : Lists.newArrayList())) : 
      		(this.gm.player == null ? Lists.newArrayList() : this.gm.playerList.keySet());
      	if(argv.length == 1 && pre.startsWith("#"))
      		s = s.substring(1);
	    for(String s1 : res) {
	        if(s1.regionMatches(true, 0, s, 0, s.length()))
	            list.add((argv.length == 1 && pre.startsWith("#") ? "#" : "") + s1);
	    }
	    return list.isEmpty() ? null : list.toArray(new String[list.size()]);
    }
	
	private List<String> getVarCompletion(String var) {
		CVar cv = this.gm.getVar(var);
		if(cv == null)
			return Lists.newArrayList();
		if(cv instanceof BoolVar)
			return Boolean.parseBoolean(cv.format()) ? Lists.newArrayList("false", "true") : Lists.newArrayList("true", "false");
		else
			return Lists.newArrayList(cv.getDefault());
//		return Lists.newArrayList();
	}
	
    private String[] sendAutocompleteRequest(String currentText)
    {
        if (currentText.length() >= 1)
        {
            BlockPos blockpos = null;
            int eid = -1;
            if (this.gm.pointed != null && this.gm.pointed.type == HitPosition.ObjectType.BLOCK)
            {
                blockpos = this.gm.pointed.block;
            }
            else if (this.gm.pointed != null && this.gm.pointed.type == HitPosition.ObjectType.ENTITY)
            {
            	eid = this.gm.pointed.entity.getId();
            	blockpos = new BlockPos(this.gm.pointed.entity);
            }
        	if(currentText.startsWith("/")) {
        		if(this.gm.player != null) {
        			currentText = currentText.substring(1);
        			this.prefixFirst = currentText.split(" ", -1).length == 1 ? "/" : null;
            		this.gm.player.client.addToSendQueue(new CPacketComplete(currentText, eid, blockpos));
                    this.waitingOnAutocomplete = true;
        		}
        	}
        	else {
        		String[] comp = this.complete(currentText);
        		this.prefixFirst = null;
        		if(comp != null)
                    this.waitingOnAutocomplete = true;
        		return comp;
        	}
        }
        return null;
    }

    private void getSentHistory(int msgPos)
    {
        int i = this.sentHistoryCursor + msgPos;
        int j = this.sentMessages.size();
        i = ExtMath.clampi(i, 0, j);

        if (i != this.sentHistoryCursor)
        {
            if (i == j)
            {
                this.sentHistoryCursor = j;
                this.inputField.setText(this.historyBuffer);
            }
            else
            {
                if (this.sentHistoryCursor == j)
                {
                    this.historyBuffer = this.inputField.getText();
                }

                this.inputField.setText(this.sentMessages.get(i));
                this.sentHistoryCursor = i;
            }
        }
    }
    
    public void onAutocompleteResponse(String[] choices)
    {
        if (this.waitingOnAutocomplete)
        {
            this.playerNamesFound = false;
            this.foundPlayerNames.clear();

            for (int z = 0; z < choices.length; z++)
            {
            	String s = choices[z];
            	if(this.prefixFirst != null)
            		choices[z] = s = this.prefixFirst + s;
                if (s.length() > 0)
                {
                    this.foundPlayerNames.add(s);
                }
            }

            String s1 = this.inputField.getText().substring(this.inputField.getSpaceBeforeCur());
            String s2 = getCommonPrefix(choices);

            if (s2.length() > 0 && !s1.equalsIgnoreCase(s2))
            {
                this.inputField.deleteSpaceToCur();
                this.inputField.insertText(s2);
            }
            else if (this.foundPlayerNames.size() > 0)
            {
                this.playerNamesFound = true;
                this.autocompletePlayerNames();
            }
        }
    }
    
    private static String getCommonPrefix(String[] strs) {
		if (strs != null && strs.length != 0) {
			int smallestIndexOfDiff = indexOfDifference(strs);
			if (smallestIndexOfDiff == -1) {
				return strs[0] == null ? "" : strs[0];
			} else {
				return smallestIndexOfDiff == 0 ? "" : strs[0].substring(0, smallestIndexOfDiff);
			}
		} else {
			return "";
		}
	}

	private static int indexOfDifference(CharSequence[] css) {
		if (css != null && css.length > 1) {
			boolean anyStringNull = false;
			boolean allStringsNull = true;
			int arrayLen = css.length;
			int shortestStrLen = Integer.MAX_VALUE;
			int longestStrLen = 0;
	
			int firstDiff;
			for (firstDiff = 0; firstDiff < arrayLen; ++firstDiff) {
				if (css[firstDiff] == null) {
					anyStringNull = true;
					shortestStrLen = 0;
				} else {
					allStringsNull = false;
					shortestStrLen = Math.min(css[firstDiff].length(), shortestStrLen);
					longestStrLen = Math.max(css[firstDiff].length(), longestStrLen);
				}
			}
	
			if (allStringsNull || longestStrLen == 0 && !anyStringNull) {
				return -1;
			} else if (shortestStrLen == 0) {
				return 0;
			} else {
				firstDiff = -1;
	
				for (int stringPos = 0; stringPos < shortestStrLen; ++stringPos) {
					char comparisonChar = css[0].charAt(stringPos);
	
					for (int arrayPos = 1; arrayPos < arrayLen; ++arrayPos) {
						if (css[arrayPos].charAt(stringPos) != comparisonChar) {
							firstDiff = stringPos;
							break;
						}
					}
	
					if (firstDiff != -1) {
						break;
					}
				}
	
				return firstDiff == -1 && shortestStrLen != longestStrLen ? shortestStrLen : firstDiff;
			}
		} else {
			return -1;
		}
	}
}
