//scans incoming text for ANSI control sequences, parses them, and returns a list of styled text runs
        public List <MUDToken> Translate(string text, MUDServerConnection connection, TelnetParser telnetParser, bool textGrouping)
        {
            if (!String.IsNullOrEmpty(text))
            {
                File.AppendAllText("D:\\temp\\output-" + logpostfix + ".txt", "<text>" + text + "</text>");
            }

            if (leftoverText != null)
            {
                text         = leftoverText + text;
                leftoverText = null;
            }

            List <MUDToken> tokens = new List <MUDToken>();

            if (string.IsNullOrEmpty(text))
            {
                return(tokens);
            }

            MUDToken        currentToken = new MUDToken();
            int             index        = 0;
            MatchCollection matches      = Regex.Matches(text, this.ansiControlRegEx);

            while (index < text.Length)
            {
                Match match = matches.OfType <Match>().SingleOrDefault(x => x.Index == index);
                if (match != null)
                {
                    if (currentToken.Content.Length > 0)
                    {
                        tokens.Add(currentToken);
                        currentToken = new MUDToken();
                    }

                    var control = new ANSIControlSequence(match.Value);
                    if (HandleANSIControl(control, currentToken, connection, telnetParser))
                    {
                        tokens.Add(currentToken);
                        currentToken = new MUDToken();
                    }

                    index += match.Length;
                }
                else
                {
                    if (text[index] == ansiControlCharacter)
                    {
                        // must have a truncated ansi control sequence, split by transmission boundaries.
                        // We have no choice but to wait for the next transmission.
                        leftoverText = text.Substring(index);
                        break;
                    }
                    else if (text[index] == '\r')
                    {
                        if (currentToken.Content.Length > 0)
                        {
                            tokens.Add(currentToken);
                            currentToken = new MUDToken();
                        }

                        currentToken.Content.Append("\r\n");
                        currentToken.TokenType = MUDTokenType.NewLine;
                        tokens.Add(currentToken);
                        currentToken = new MUDToken();

                        if (index == text.Length - 1)
                        {
                            break;
                        }
                        else
                        {
                            index++;
                        }
                    }
                    else if (text[index] == '\n' && index == 0)
                    {
                        // must have split up an "\r\n" along transmission boundaries, which means it was added at the last
                        // loop. Just ignore this one then.
                        index++;
                        continue;
                    }
                    else if (text[index] == '\b')
                    {
                        // backspace. Christ.
                        if (currentToken.Content.Length > 0)
                        {
                            currentToken.Content.Remove(currentToken.Content.Length - 1, 1);
                        }
                    }
                    else
                    {
                        currentToken.Content.Append(ReplaceSpecialCharacters(text[index]));
                    }

                    index++;
                }
            }

            if (currentToken.Content.Length > 0)
            {
                if (currentToken.TokenType == MUDTokenType.Text && textGrouping == true && currentToken.String != "(N)onstop, (Q)uit, or (C)ontinue?")
                {
                    // If "text grouping" is enabled then we don't submit any text blocks if they are last. We'll wait for the next non-text
                    // transmission to submit it.
                    if (leftoverText != null)
                    {
                        leftoverText = currentToken.String + leftoverText;
                    }
                    else
                    {
                        leftoverText = currentToken.String;
                    }
                }
                else
                {
                    tokens.Add(currentToken);
                }
            }

            var strings = from token in tokens
                          select String.Format("<{0}>{1}</{0}>", token.TokenType, token.TokenType == MUDTokenType.NewLine? "\\r\\n" : token.String);

            File.AppendAllLines("D:\\temp\\tokens-" + logpostfix + ".txt", strings);

            return(tokens);
        }
        private bool HandleANSIControl(ANSIControlSequence control, MUDToken token, MUDServerConnection connection, TelnetParser telnetParser)
        {
            bool handled = false;

            if (control.Operation == 'm')
            {
                token.TokenType = MUDTokenType.Color;
                handled         = true;
            }
            else if (control.Operation == 'n')
            {
                // Query cursor position. Respond with an arbitrary value for now.
                // TODO: figure out if we really need to handle this better.
                if (control.Arguments.Count == 1 && control.Arguments[0] == 6)
                {
                    telnetParser.sendTelnetBytes(ASCIIEncoding.ASCII.GetBytes("\x1b[4;6R"));
                }
                handled = false;
            }
            else if (control.Operation == '!')
            {
                // seriously don't have a clue what this does.
                handled = false;
            }
            else if (control.Operation == 'J')
            {
                // clear text, line, screen. Either way, just ignore it.
                handled = false;
            }
            else if (control.Operation == 'K')
            {
                token.TokenType = MUDTokenType.EraseLine;
                handled         = true;
            }
            else if (control.Operation == 'D')
            {
                token.TokenType = MUDTokenType.CursorBackward;
                handled         = true;
            }
            else if (control.Operation == 'A' ||
                     control.Operation == 'B' ||
                     control.Operation == 'C' ||
                     control.Operation == 'H')
            {
                // cursor stuff, just ignore it.
                handled = false;
            }
            else
            {
                handled = false;
            }


            if (handled)
            {
                token.Content.Append(control.RawText);
                token.Arguments.AddRange(control.Arguments);
                return(true);
            }

            return(false);
        }