package com.planet_ink.siplet.support;

import java.io.*;
import java.util.*;

import com.jcraft.jzlib.*;
import com.planet_ink.coffee_mud.core.CMProps;
import com.planet_ink.siplet.applet.Siplet;
import com.planet_ink.siplet.applet.Siplet.MSPStatus;
import com.planet_ink.siplet.support.MiniJSON.JSONObject;
import com.planet_ink.siplet.support.MiniJSON.MJSONException;

/*
   Copyright 2005-2020 Bo Zimmerman

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
public class TelnetFilter
{
	public final static boolean	debugChars				= false;
	public final static boolean	debugTelnetCodes		= false;

	protected static final char	IAC_SE					= 240;
	protected static final char	IAC_					= 255;
	protected static final char	IAC_SB					= 250;
	protected static final char	IAC_DO					= 253;
	protected static final char	IAC_WILL				= 251;
	protected static final char	IAC_WONT				= 252;
	protected static final char	IAC_DONT				= 254;
	protected static final char	IAC_MSDP				= 69;
	protected static final char	IAC_GMCP				= 201;
	protected static final char	IAC_MSP					= 90;
	protected static final char	IAC_MXP					= 91;
	protected static final char	IAC_GA					= 249;
	protected static final char	IAC_NOP					= 241;
	protected static final char	TELNET_ATCP				= 200;
	protected static final char	TELNET_GMCP				= 201;
	protected static final char	TELOPT_BINARY			= 0;
	protected static final char	TELOPT_EOR				= 25;
	protected static final char	TELOPT_ECHO				= 1;
	protected static final char	TELOPT_NAWS				= 31;
	protected static final char	TELOPT_LOGOUT			= 18;
	protected static final char	TELOPT_TTYPE			= 24;
	protected static final char	TELOPT_TSPEED			= 32;
	protected static final char	MCCP_COMPRESS			= 85;
	protected static final char	MCCP_COMPRESS2			= 86;
	protected static final char	TELOPT_NEWENVIRONMENT	= 39;

	protected String			lastBackground			= null;
	protected String			lastForeground			= null;
	protected boolean			blinkOn					= false;
	protected boolean			fontOn					= false;
	protected boolean			boldOn					= false;
	protected boolean			underlineOn				= false;
	protected boolean			italicsOn				= false;
	private Siplet				codeBase				= null;
	protected boolean			comment					= false;

	protected MSPStatus			neverSupportMSP			= MSPStatus.Internal;
	protected boolean			neverSupportMXP			= false;
	protected boolean			neverSupportMSDP		= false;
	protected boolean			neverSupportGMCP		= false;
	protected boolean			neverSupportMCCP		= false;
	protected boolean			MSPsupport				= false;
	protected boolean			MSDPsupport				= false;
	protected boolean			GMCPsupport				= false;
	protected boolean			MXPsupport				= false;
	protected boolean			MCCPsupport				= false;
	private final StringBuilder	msdpInforms				= new StringBuilder("");
	private final StringBuilder	gmcpInforms				= new StringBuilder("");

	private final MSP			mspModule				= new MSP();
	private final MSDP			msdpModule				= new MSDP();
	private final GMCP			gmcpModule				= new GMCP();
	private final MXP			mxpModule				= new MXP();

	private TelnetFilter()
	{
	}

	public TelnetFilter(final Siplet codebase)
	{
		this();
		codeBase = codebase;
	}

	public static String getSipletVersion()
	{
		return Siplet.VERSION_MAJOR + "." + Siplet.VERSION_MINOR;
	}

	public String getEnquedResponses()
	{
		return (MXPsupport() ? mxpModule.getAnyResponses() : "");
	}

	public String getEnquedJScript()
	{
		return ((mxpModule != null) ? mxpModule.getAnyJScript() : "")
				+ ((mspModule != null) ? mspModule.getAnyJScript() : "")
				+ ((gmcpModule != null) ? gmcpModule.getAnyJScript() : "");
	}

	public boolean MSPsupport()
	{
		return MSPsupport;
	}

	public void setMSPSupport(final boolean truefalse)
	{
		MSPsupport = truefalse;
	}

	public boolean MXPsupport()
	{
		return MXPsupport;
	}

	public void setMXPSupport(final boolean truefalse)
	{
		MXPsupport = truefalse;
	}

	public boolean MCCPsupport()
	{
		return MCCPsupport;
	}

	public void setMCCPSupport(final boolean truefalse)
	{
		MCCPsupport = truefalse;
	}

	public boolean MSDPsupport()
	{
		return MSDPsupport;
	}

	public void setMSDPSupport(final boolean truefalse)
	{
		MSDPsupport = truefalse;
	}

	public boolean GMCPsupport()
	{
		return GMCPsupport;
	}

	public void setGMCPSupport(final boolean truefalse)
	{
		GMCPsupport = truefalse;
	}

	public void setNeverMXPSupport(final boolean truefalse)
	{
		neverSupportMXP = truefalse;
	}

	public void setNeverMSPSupport(final MSPStatus status)
	{
		neverSupportMSP = status;
	}

	public void setNeverMCCPSupport(final boolean truefalse)
	{
		neverSupportMCCP = truefalse;
	}

	public void setNeverMSDPSupport(final boolean truefalse)
	{
		neverSupportMSDP = truefalse;
	}

	public void setNeverGMCPSupport(final boolean truefalse)
	{
		neverSupportGMCP = truefalse;
	}

	public boolean isUIonHold()
	{
		return MXPsupport() && mxpModule.isUIonHold();
	}

	private String blinkOff()
	{
		if (blinkOn)
		{
			blinkOn = false;
			return "</BLINK>";
		}
		return "";
	}

	private String underlineOff()
	{
		if (underlineOn)
		{
			underlineOn = false;
			return "</U>";
		}
		return "";
	}

	private String fontOff()
	{
		if (fontOn)
		{
			setLastBackground(ColorSupport.defaultBackground);
			setLastForeground(ColorSupport.defaultForeground);
			fontOn = false;
			return "</FONT>";
		}
		return "";
	}

	private String italicsOff()
	{
		if (italicsOn)
		{
			italicsOn = false;
			return "</I>";
		}
		return "";
	}

	private String allOff()
	{
		final StringBuffer off = new StringBuffer("");
		off.append(blinkOff());
		off.append(underlineOff());
		off.append(fontOff());
		off.append(italicsOff());
		return off.toString();
	}

	public String getMsdpHtml()
	{
		synchronized (msdpInforms)
		{
			if (msdpInforms.length() == 0)
				return "";
			final String bah = msdpInforms.toString();
			msdpInforms.setLength(0);
			return "<BR><PRE>" + bah + "</PRE><BR>";
		}
	}

	public String getGmcpHtml()
	{
		synchronized (gmcpInforms)
		{
			if (gmcpInforms.length() == 0)
				return "";
			final String bah = gmcpInforms.toString();
			gmcpInforms.setLength(0);
			return "<BR><PRE>" + bah + "</PRE>";
		}
	}

	public static int getColorCodeIndex(String word)
	{
		if (word == null)
			word = ColorSupport.defaultForeground;
		for (int i = 0; i < ColorSupport.colorCodes1.length; i++)
		{
			if (word.equalsIgnoreCase(ColorSupport.colorCodes1[i]))
				return (40 + i);
		}
		for (int i = 0; i < ColorSupport.colorCodes2.length; i++)
		{
			if (word.equalsIgnoreCase(ColorSupport.colorCodes2[i]))
				return (30 + i);
		}
		return 30;
	}

	public static int getRelativeColorCodeIndex(final String word)
	{
		final int x = getColorCodeIndex(word);
		if (x < 40)
			return x - 30;
		if (x > 50)
			return x % 10;
		return x - 40;
	}

	private void setLastBackground(final String val)
	{
		if (MXPsupport())
			mxpModule.lastBackground = val;
		else
			lastBackground = val;
	}

	private void setLastForeground(final String val)
	{
		if (MXPsupport())
			mxpModule.lastForeground = val;
		else
			lastForeground = val;
	}

	private String lastBackground()
	{
		return MXPsupport() ? mxpModule.lastBackground : lastBackground;
	}

	private String lastForeground()
	{
		return MXPsupport() ? mxpModule.lastForeground : lastForeground;
	}

	private String escapeTranslate(final String escapeString)
	{
		if (escapeString.endsWith("m"))
		{
			final Vector<String> V = Util.parseSemicolons(escapeString.substring(0, escapeString.length() - 1), true);
			final StringBuffer str = new StringBuffer("");
			String s = null;
			int code = 0;
			String background = null;
			String foreground = null;
			for (int i = 0; i < V.size(); i++)
			{
				s = V.elementAt(i);
				code = Util.s0_int(s);
				switch (code)
				{
				case 0:
					if (i == (V.size() - 1))
						str.append(allOff());
					boldOn = false;
					break;
				case 1:
					boldOn = true;
					if ((V.size() == 1) && (lastForeground() != null))
						foreground = ColorSupport.colorCodes2[getRelativeColorCodeIndex(lastForeground())];
					break;
				case 4:
				{
					if (!underlineOn)
					{
						underlineOn = true;
						str.append("<U>");
					}
					break;
				}
				case 5:
				{
					if (!blinkOn)
					{
						blinkOn = true;
						str.append("<BLINK>");
					}
					break;
				}
				case 6:
				{
					if (!italicsOn)
					{
						italicsOn = true;
						str.append("<I>");
					}
					break;
				}
				case 7:
				{
					// this is reverse on, and requires a wierd color reversal
					// from whatever the previous colors were.
					// do it later
					break;
				}
				case 8:
				{
					background = ColorSupport.defaultBackground;
					foreground = ColorSupport.defaultBackground;
					break;
				}
				case 22:
					str.append(allOff());
					break;
				case 24:
					str.append(underlineOff());
					break;
				case 25:
					str.append(blinkOff());
					break;
				case 26:
					str.append(italicsOff());
					break;
				case 30:
				case 31:
				case 32:
				case 33:
				case 34:
				case 35:
				case 36:
				case 37:
					foreground = boldOn ? ColorSupport.colorCodes2[code - 30] : ColorSupport.colorCodes1[code - 30];
					break;
				case 39:
					foreground = ColorSupport.defaultForeground;
					break;
				case 40:
				case 41:
				case 42:
				case 43:
				case 44:
				case 45:
				case 46:
				case 47:
					background = ColorSupport.colorCodes1[code - 40];
					break;
				case 49:
					background = ColorSupport.defaultForeground;
					break;
				case 38: // foreground ansi-256
					if((i<V.size()-2)
					&&(Util.s_int(V.get(i+1))==5))
					{
						final int cd=Util.s0_int(V.get(i+2));
						if((cd >=0)
						&& (cd < ColorSupport.html256.length)
						&&(ColorSupport.html256[cd].length()>0))
							foreground = ColorSupport.html256[cd];
						i=V.size()-1;
					}
					break;
				case 48: // background ansi 256
					if((i<V.size()-2)
					&&(Util.s_int(V.get(i+1))==5))
					{
						final int cd=Util.s0_int(V.get(i+2));
						if((cd >=0)
						&& (cd < ColorSupport.html256.length)
						&&(ColorSupport.html256[cd].length()>0))
							background = ColorSupport.html256[cd];
						i=V.size()-1;
					}
					break;
				}
				if ((background != null) || (foreground != null))
				{
					if (lastBackground() == null)
						setLastBackground(ColorSupport.defaultBackground);
					if (lastForeground() == null)
						setLastForeground(ColorSupport.defaultForeground);
					if (background == null)
						background = lastBackground();
					if (foreground == null)
						foreground = lastForeground();

					if ((!lastBackground().equals(background)) || (!lastForeground().equals(foreground)))
					{
						str.append(fontOff());
						setLastBackground(background);
						setLastForeground(foreground);
						fontOn = true;
						if (MXPsupport())
							str.append("<FONT COLOR=" + foreground + " BACK=" + background + ">");
						else
							str.append("<FONT STYLE=\"color: " + foreground + ";background-color: " + background + "\">");
					}
				}
			}
			return str.toString();
		}
		return escapeString;
	}

	public static final char[]	mccppattern	= { IAC_, IAC_SB, MCCP_COMPRESS2, IAC_, IAC_SE };
	public int					patDex		= 0;

	public void TelnetRead(final StringBuffer buf, final InputStream rawin, final Reader in[]) throws InterruptedIOException, IOException
	{
		final char c = (char) in[0].read();
		if (mccppattern[patDex] == c)
		{
			patDex++;
			if ((patDex >= mccppattern.length) && (!neverSupportMCCP))
			{
				while (rawin.available() > 0)
					rawin.read();
				final ZInputStream zIn = new ZInputStream(rawin);
				if (debugTelnetCodes)
					System.out.println("MCCP compression started");
				in[0] = new IACReader(zIn, CMProps.getVar(CMProps.Str.CHARSETINPUT));
				patDex = 0;
			}
			return;
		}
		else
		if (patDex > 0)
		{
			for (int i = 0; i < patDex; i++)
				buf.append(mccppattern[i]);
			patDex = 0;
		}
		buf.append(c);
		if (c == 65535)
			throw new java.io.InterruptedIOException("ARGH!");
	}

	public int TelenetFilter(final StringBuffer buf, final DataOutputStream response, final InputStream rawin, final Reader[] in) throws IOException
	{
		int i = 0;
		while (i < buf.length())
		{
			switch (buf.charAt(i))
			{
			case 0:
				buf.delete(i, i + 1);
				break;
			case IAC_:
			{
				if (debugTelnetCodes)
					System.out.println("Got IAC in " + i + "/" + buf.length());
				if (i >= buf.length() - 2)
					return i;
				if (debugTelnetCodes)
					System.out.println("Receiving " + (int) buf.charAt(i + 1));
				final int oldI = i;
				int end = oldI + 3;
				switch (buf.charAt(++i))
				{
				case IAC_SB:
				{
					final ByteArrayOutputStream subOptionData = new ByteArrayOutputStream();
					final int subOptionCode = buf.charAt(++i);
					if (debugTelnetCodes)
						System.out.println("Got sub-option " + subOptionCode);
					int last = 0;
					while ((i < (buf.length() - 1)) && ((last = buf.charAt(++i)) != -1))
					{
						if ((last == IAC_) && (i < (buf.length() - 1)))
						{
							last = buf.charAt(++i);
							if (last == IAC_)
							{
								subOptionData.write(IAC_); // this is iac iac --
															// escape, dup type
															// thing?
							}
							else
							if (last == IAC_SE)
								break;
						}
						else
							subOptionData.write((char) last);
					}
					end = i + 1;
					if (debugTelnetCodes)
						System.out.println("Got SB " + subOptionCode);
					if (subOptionCode == TELOPT_TTYPE)
					{
						if (debugTelnetCodes)
							System.out.println("Responding with termtype.");
						final byte[] data = new byte[6];
						data[0] = (short) 's';
						data[1] = (short) 'i';
						data[2] = (short) 'p';
						data[3] = (short) 'l';
						data[4] = (short) 'e';
						data[5] = (short) 't';
						response.writeBytes("" + IAC_ + IAC_SB + TELOPT_TTYPE + (char) 0);
						response.write(data);
						response.writeBytes("" + IAC_ + IAC_SE);
						response.flush();
					}
					else
					if (subOptionCode == TELOPT_NAWS)
					{
						if (debugTelnetCodes)
							System.out.println("Responding with screen size.");
						final byte[] data = new byte[4];
						data[1] = 80;
						data[3] = 25;
						response.writeBytes("" + IAC_ + IAC_SB + TELOPT_NAWS);
						response.write(data);
						response.writeBytes("" + IAC_ + IAC_SE);
						response.flush();
					}
					else
					if (subOptionCode == MCCP_COMPRESS2)
					{
						// probably need to handle this earlier
					}
					else
					if (subOptionCode == IAC_MSDP)
					{
						final String received = this.msdpModule.msdpReceive(subOptionData.toByteArray());
						synchronized (msdpInforms)
						{
							msdpInforms.append(received);
						}
						if (debugTelnetCodes)
							System.out.println("Got MSDP: " + received);
					}
					else
					if (subOptionCode == IAC_GMCP)
					{
						final String received = this.gmcpModule.gmcpReceive(subOptionData.toByteArray());
						synchronized (gmcpInforms)
						{
							gmcpInforms.append(received + "\n");
						}
						if (debugTelnetCodes)
							System.out.println("Got GMCP: " + received);
					}
					break;
				}
				case IAC_WILL:
					i++;
					if (buf.charAt(i) == TELOPT_NAWS)
					{
						if (debugTelnetCodes)
							System.out.println("Responding with screen size to WILL NAWS.");
						final byte[] data = new byte[4];
						data[1] = 80;
						data[3] = 25;
						response.writeBytes("" + IAC_ + IAC_SB + TELOPT_NAWS);
						response.write(data);
						response.writeBytes("" + IAC_ + IAC_SE);
						break;
					}
					else
					if (buf.charAt(i) == IAC_MSP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WILL MSP!");
						if (neverSupportMSP == MSPStatus.Disabled)
						{
							if (MSPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent DONT MSP!");
								response.writeBytes("" + IAC_ + IAC_DONT + IAC_MSP);
								response.flush();
								setMSPSupport(false);
							}
						}
						else
						if (!MSPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DO MSP!");
							response.writeBytes("" + IAC_ + IAC_DO + IAC_MSP);
							response.flush();
							setMSPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == IAC_MSDP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WILL MSDP!");
						if (neverSupportMSDP)
						{
							if (MSDPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent DONT MSDP!");
								response.writeBytes("" + IAC_ + IAC_DONT + IAC_MSDP);
								response.flush();
								setMSDPSupport(false);
							}
						}
						else
						if (!MSDPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DO MSDP!");
							response.writeBytes("" + IAC_ + IAC_DO + IAC_MSDP);
							response.flush();
							setMSDPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == IAC_GMCP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WILL GMCP!");
						if (neverSupportGMCP)
						{
							if (GMCPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent DONT GMCP!");
								response.writeBytes("" + IAC_ + IAC_DONT + IAC_GMCP);
								response.flush();
								setGMCPSupport(false);
							}
						}
						else
						if (!GMCPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DO GMCP!");
							response.writeBytes("" + IAC_ + IAC_DO + IAC_GMCP);
							response.flush();
							setGMCPSupport(true);
							response.writeBytes("" + IAC_ + IAC_SB + IAC_GMCP);
							response.writeBytes("core.hello {\"client\":\"siplet\",\"version\":" + Siplet.VERSION_MAJOR + "}");
							response.writeBytes("" + IAC_ + IAC_SE);
						}
					}
					else
					if (buf.charAt(i) == IAC_MXP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WILL MXP!");
						if (neverSupportMXP)
						{
							if (MXPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Send DONT MXP!");
								response.writeBytes("" + IAC_ + IAC_DONT + IAC_MXP);
								response.flush();
								setMXPSupport(false);
							}
						}
						else
						if (!MXPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Send DO MXP!");
							response.writeBytes("" + IAC_ + IAC_DO + IAC_MXP);
							response.flush();
							setMXPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == MCCP_COMPRESS2)
					{
						if (debugTelnetCodes)
							System.out.println("Got WILL COMPRESS2!");
						if (neverSupportMCCP)
						{
							if (MCCPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Send DONT COMPRESS2!");
								response.writeBytes("" + IAC_ + IAC_DONT + MCCP_COMPRESS2);
								response.flush();
								setMXPSupport(false);
							}
						}
						else
						if (!MCCPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Send DO MCCP!");
							response.writeBytes("" + IAC_ + IAC_DO + MCCP_COMPRESS2);
							response.flush();
							setMCCPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == TELOPT_LOGOUT)
					{
						// goot for you serverdude
					}
					else
					if (buf.charAt(i) != TELOPT_BINARY)
					{
						if (debugTelnetCodes)
							System.out.println("Sent DONT " + ((int) buf.charAt(i)) + "!");
						response.writeBytes("" + IAC_ + IAC_DONT + buf.charAt(i));
						response.flush();
					}
					break;
				case IAC_WONT:
					i++;
					if (buf.charAt(i) == IAC_MSP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WONT MSP!");
						if (MSPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DONT MSP!");
							response.writeBytes("" + IAC_ + IAC_DONT + IAC_MSP);
							response.flush();
							setMSPSupport(false);
						}
					}
					else
					if (buf.charAt(i) == IAC_MSDP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WONT MSDP!");
						if (MSDPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DONT MSDP!");
							response.writeBytes("" + IAC_ + IAC_DONT + IAC_MSDP);
							response.flush();
							setMSDPSupport(false);
						}
					}
					else
					if (buf.charAt(i) == IAC_GMCP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WONT GMCP!");
						if (GMCPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DONT GMCP!");
							response.writeBytes("" + IAC_ + IAC_DONT + IAC_GMCP);
							response.flush();
							setGMCPSupport(false);
						}
					}
					else
					if (buf.charAt(i) == IAC_MXP)
					{
						if (debugTelnetCodes)
							System.out.println("Got WONT MXP!");
						if (MXPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent DONT MXP!");
							response.writeBytes("" + IAC_ + IAC_DONT + IAC_MXP);
							response.flush();
							setMXPSupport(false);
							if (mxpModule != null)
								mxpModule.shutdownMXP();
						}
					}
					break;
				case IAC_DO:
					i++;
					if (buf.charAt(i) == IAC_MSP)
					{
						if (debugTelnetCodes)
							System.out.println("Got DO MSP!");
						if (neverSupportMSP == MSPStatus.Disabled)
						{
							if (MSPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent WONT MSP!");
								response.writeBytes("" + IAC_ + IAC_WONT + IAC_MSP);
								response.flush();
								setMSPSupport(false);
							}
						}
						else
						if (!MSPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent WILL MSP!");
							response.writeBytes("" + IAC_ + IAC_WILL + IAC_MSP);
							response.flush();
							setMSPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == IAC_GMCP)
					{
						if (debugTelnetCodes)
							System.out.println("Got DO GMCP!");
						if (neverSupportGMCP)
						{
							if (GMCPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent WONT GMCP!");
								response.writeBytes("" + IAC_ + IAC_WONT + IAC_GMCP);
								response.flush();
								setGMCPSupport(false);
							}
						}
						else
						if (!GMCPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent WILL GMCP!");
							response.writeBytes("" + IAC_ + IAC_WILL + IAC_GMCP);
							response.flush();
							setGMCPSupport(true);
						}
					}
					else
					if (buf.charAt(i) == TELOPT_LOGOUT)
					{
						// good for you serverdude
					}
					else
					if (buf.charAt(i) == IAC_MXP)
					{
						if (debugTelnetCodes)
							System.out.println("Got DO MXP!");
						if (neverSupportMXP)
						{
							if (MXPsupport())
							{
								if (debugTelnetCodes)
									System.out.println("Sent WONT MXP!");
								response.writeBytes("" + IAC_ + IAC_WONT + IAC_MXP);
								response.flush();
								setMXPSupport(false);
							}
						}
						else
						if (!MXPsupport())
						{
							response.writeBytes("" + IAC_ + IAC_WILL + IAC_MXP);
							if (debugTelnetCodes)
								System.out.println("Sent WILL MXP!");
							response.flush();
							setMXPSupport(true);
						}
					}
					else
					if (buf.charAt(i) != TELOPT_BINARY)
					{
						if (debugTelnetCodes)
							System.out.println("Send WONT " + (int) buf.charAt(i) + "!");
						response.writeBytes("" + IAC_ + IAC_WONT + buf.charAt(i));
						response.flush();
					}
					break;
				case IAC_DONT:
					i++;
					if (buf.charAt(i) == IAC_MSP)
					{
						if (debugTelnetCodes)
							System.out.println("Got DONT MSP!");
						if (MSPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent WONT MSP!");
							response.writeBytes("" + IAC_ + IAC_WONT + IAC_MSP);
							response.flush();
							setMSPSupport(false);
						}
					}
					else
					if (buf.charAt(i) == IAC_MXP)
					{
						if (MXPsupport())
						{
							if (debugTelnetCodes)
								System.out.println("Sent WONT MXP!");
							response.writeBytes("" + IAC_ + IAC_WONT + IAC_MXP);
							response.flush();
							setMXPSupport(false);
							if (mxpModule != null)
								mxpModule.shutdownMXP();
						}
					}
					break;
				case IAC_GA:
				case IAC_NOP:
					end = oldI + 2;
					break;
				}
				buf.delete(oldI, end);
				i = oldI - 1;
				break;
			}
			}
			i++;
		}
		return buf.length();
	}

	// filters out color codes -> <FONT>
	// CRS -> <BR>
	// SPACES -> &nbsp;
	// < -> &lt;
	// TELNET codes -> response outputstream
	public int HTMLFilter(final StringBuffer buf)
	{
		int i = 0;
		final boolean[] eolEater = new boolean[1];
		while (i < buf.length())
		{
			if (debugChars)
				System.out.println(">" + buf.charAt(i));
			if (comment)
			{
				if (((i + 3) < buf.length()) && buf.substring(i, i + 3).equals("-->"))
				{
					comment = false;
					i += 3;
				}
			}
			else
			if ((MXPsupport() && (mxpModule.eatTextUntilNextEOLN()) && (buf.charAt(i) != '\n') && (buf.charAt(i) != '\r')))
			{
				buf.deleteCharAt(i);
				i--;
			}
			else
				switch (buf.charAt(i))
				{
				case '!':
					if ((i < buf.length() - 3) && (buf.charAt(i + 1) == '!'))
					{
						if (MSPsupport())
						{
							final int endl = mspModule.process(buf, i, codeBase, neverSupportMSP == MSPStatus.External);
							if (endl == -1)
								i--;
							else
							if (endl > 0)
								return endl;
						}
					}
					break;
				case '&':
				{
					if (!MXPsupport())
					{
						buf.insert(i + 1, "amp;");
						i += 4;
					}
					else
					{
						final int x = mxpModule.processEntity(buf, i, null, true);
						if (x == Integer.MAX_VALUE)
							return i;
						i += x;
					}
					break;
				}
				case ' ':
					buf.setCharAt(i, '&');
					buf.insert(i + 1, "nbsp;");
					i += 5;
					break;
				case '>':
					buf.setCharAt(i, '&');
					buf.insert(i + 1, "gt;");
					i += 3;
					break;
				case '<':
					if (!MXPsupport())
					{
						buf.setCharAt(i, '&');
						buf.insert(i + 1, "lt;");
						i += 3;
					}
					else
					if (((i + 4) < buf.length()) && (buf.substring(i + 1, i + 4).equals("!--")))
						comment = true;
					else
					{
						final int x = mxpModule.processTag(buf, i);
						if (x == Integer.MAX_VALUE)
							return i;
						i += x;
					}
					break;
				case '\n':
				{
					if (MXPsupport())
					{
						final int x = mxpModule.newlineDetected(buf, i + 1, eolEater);
						if (eolEater[0])
							buf.deleteCharAt(i);
						else
						{
							buf.setCharAt(i, '<');
							buf.insert(i + 1, "BR>");
							i += 3;
						}
						i += x;
					}
					else
					{
						buf.setCharAt(i, '<');
						buf.insert(i + 1, "BR>");
						i += 3;
					}
					break;
				}
				case '\r':
					buf.deleteCharAt(i);
					i--;
					break;
				case IAC_:
				{
					if (i >= buf.length() - 3)
						return i;
					break;
				}
				case '\033':
				{
					final int savedI = i;
					if (i == buf.length() - 1)
						return i;
					if (buf.charAt(++i) != '[')
						buf.setCharAt(i, ' ');
					else
					{
						boolean quote = false;
						while (((++i) < buf.length()) && ((quote) || (!Character.isLetter(buf.charAt(i)))))
						{
							if (buf.charAt(i) == '"')
								quote = !quote;
						}
						if (i == buf.length())
							return savedI;
						final String oldStr = buf.substring(savedI + 2, i + 1);
						final String translate = escapeTranslate(oldStr);
						if (translate.equals(oldStr))
						{
							final int x = mxpModule.escapeTranslate(oldStr, buf, savedI);
							if (x == Integer.MAX_VALUE)
								return i;
							i = savedI + x;
						}
						else
						if (!translate.equals(oldStr))
						{
							buf.replace(savedI, i + 1, translate);
							i = savedI + translate.length() - 1;
						}
					}
				}
					break;
				}
			i++;
		}
		return buf.length();
	}

	public final byte[] peruseInput(final String data)
	{
		if (data == null)
			return null;
		try
		{
			if (data.startsWith("\\"))
			{
				final int x = data.indexOf(' ');
				if (x < 0)
					return data.getBytes("UTF-8");
				final String cmd = data.substring(1, x).toUpperCase().trim();
				final String rest = data.substring(x + 1).trim();
				if (cmd.equalsIgnoreCase("MSDP"))
				{
					try
					{
						final byte[] newOutput = this.msdpModule.convertStringToMsdp(rest);
						if (newOutput != null)
							return newOutput;
					}
					catch (final MJSONException e)
					{
						if (debugTelnetCodes)
							System.out.println("JSON Parse Error: " + e.getMessage());
					}
					return null;
				}
				else
				if (cmd.equalsIgnoreCase("GMCP"))
				{
					try
					{
						final byte[] newOutput = gmcpModule.convertStringToGmcp(rest);
						if (newOutput != null)
							return newOutput;
					}
					catch (final MJSONException e)
					{
						if (debugTelnetCodes)
							System.out.println("JSON Parse Error: " + e.getMessage());
					}
					return null;
				}
				else
					return data.getBytes("UTF-8");
			}
			return data.getBytes("UTF-8");
		}
		catch (final UnsupportedEncodingException e)
		{
			return data.getBytes();
		}
	}
}
