using System;
using System.Collections.Generic;

namespace Helix.Tests
{
    public class AnsiStringParser
    {
        private LinkedList<AnsiToken> _tokens = new LinkedList<AnsiToken>();
        private ColorContext _colorContext = new ColorContext();
        private bool _colorChanged = false;

        public AnsiStringParser()
        {
            _tokens.AddLast(new AnsiClear());
        }

        private void PutLetter(char c)
        {
            if (_colorChanged)
            {
                _tokens.AddLast(new AnsiColor(_colorContext.Clone()));
                _colorChanged = false;
            }
            _tokens.AddLast(new AnsiLetter(c));
        }

        private void PutWord()
        {
        }

        private void PutColor(Color color)
        {
            ChangeColor(x => _colorContext.Color, x => _colorContext.Color = color);
        }

        private void SetBold()
        {
            ChangeColor(x => _colorContext.Bold, x => _colorContext.Bold = true);
        }

        private void RemoveBold()
        {
            ChangeColor(x => _colorContext.Bold, x => _colorContext.Bold = false);
        }

        private void SetInverse()
        {
            ChangeColor(x => _colorContext.Inverse, x => _colorContext.Inverse = true);
        }

        private void RemoveInverse()
        {
            ChangeColor(x => _colorContext.Inverse, x => _colorContext.Inverse = false);
        }

        private void SetUnderline()
        {
            ChangeColor(x => _colorContext.Underline, x => _colorContext.Underline = true);
        }

        private void RemoveUnderline()
        {
            ChangeColor(x => _colorContext.Underline, x => _colorContext.Underline = false);
        }

        private void ChangeColor<T>(Func<ColorContext, T> func, Action<ColorContext> action)
        {
            var previous = func(_colorContext);

            action(_colorContext);
            if (!previous.Equals(_colorContext))
            {
                _colorChanged = true;
            }
        }

        public IEnumerable<AnsiToken> Parse(string buffer)
        {
            // var reader = new StreamReader(new MemoryStream(Encoding.UTF8))
            var escape = false;

            foreach (var c in buffer)
            {
                switch (escape)
                {
                    case true:
                        if (c == '#')
                        {
                            PutLetter('#');
                        }

                        // color
                        switch (c)
                        {
                            case 'r':
                                PutColor(Color.Red);
                                RemoveBold();
                                break;
                            case 'g':
                                PutColor(Color.Green);
                                RemoveBold();
                                break;
                            case 'b':
                                PutColor(Color.Blue);
                                RemoveBold();
                                break;
                            case 'c':
                                PutColor(Color.Cyan);
                                RemoveBold();
                                break;
                            case 'm':
                                PutColor(Color.Magenta);
                                RemoveBold();
                                break;
                            case 'y':
                                PutColor(Color.Yellow);
                                RemoveBold();
                                break;
                            case 'k':
                                PutColor(Color.Blue);
                                RemoveBold();
                                break;
                            case 'w':
                                PutColor(Color.White);
                                RemoveBold();
                                break;
                            case 'R':
                                PutColor(Color.Red);
                                SetBold();
                                break;
                            case 'G':
                                PutColor(Color.Green);
                                SetBold();
                                break;
                            case 'B':
                                PutColor(Color.Blue);
                                SetBold();
                                break;
                            case 'C':
                                PutColor(Color.Cyan);
                                SetBold();
                                break;
                            case 'M':
                                PutColor(Color.Magenta);
                                SetBold();
                                break;
                            case 'Y':
                                PutColor(Color.Yellow);
                                SetBold();
                                break;
                            case 'K':
                                PutColor(Color.Blue);
                                SetBold();
                                break;
                            case 'W':
                                PutColor(Color.White);
                                SetBold();
                                break;
                        }

                        // flags
                        switch (c)
                        {
                            case 'o':
                                RemoveBold();
                                break;
                            case 'O':
                                SetBold();
                                break;
                            case 'i':
                                RemoveInverse();
                                break;
                            case 'I':
                                SetInverse();
                                break;
                            case 'u':
                                RemoveUnderline();
                                break;
                            case 'U':
                                SetUnderline();
                                break;
                            case 'n':
                                // Restore()
                                RemoveBold();
                                RemoveInverse();
                                RemoveUnderline();
                                PutColor(Color.White);
                                break;
                        }

                        escape = false;
                        break;
                    case false:
                        if (c == '#')
                        {
                            escape = true;
                            continue;
                        }
                        // else if (c == ' ')
                        // {
                        //     PutWord();
                        // }
                        else
                        {
                            PutLetter(c);
                        }
                        break;
                }
            }
            PutWord();
            _tokens.AddLast(new AnsiClear());
            return _tokens;
        }
    }
}