using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Discord.Commands
{
    internal class CommandMapNode
    {
        private readonly ConcurrentDictionary<string, CommandMapNode> _nodes;
        private readonly string _name;
        private readonly object _lockObj = new object();
        private ImmutableArray<Command> _commands;

        public bool IsEmpty => _commands.Length == 0 && _nodes.Count == 0;

        public CommandMapNode(string name)
        {
            _name = name;
            _nodes = new ConcurrentDictionary<string, CommandMapNode>();
            _commands = ImmutableArray.Create<Command>();
        }

        public void AddCommand(string text, int index, Command command)
        {
            int nextSpace = text.IndexOf(' ', index);
            string name;

            lock (_lockObj)
            {
                if (text == "")
                    _commands = _commands.Add(command);
                else
                {
                    if (nextSpace == -1)
                        name = text.Substring(index);
                    else
                        name = text.Substring(index, nextSpace - index);

                    var nextNode = _nodes.GetOrAdd(name, x => new CommandMapNode(x));
                    nextNode.AddCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command);
                }
            }
        }
        public void RemoveCommand(string text, int index, Command command)
        {
            int nextSpace = text.IndexOf(' ', index);
            string name;

            lock (_lockObj)
            {
                if (text == "")
                    _commands = _commands.Remove(command);
                else
                {
                    if (nextSpace == -1)
                        name = text.Substring(index);
                    else
                        name = text.Substring(index, nextSpace - index);

                    CommandMapNode nextNode;
                    if (_nodes.TryGetValue(name, out nextNode))
                    {
                        nextNode.RemoveCommand(nextSpace == -1 ? "" : text, nextSpace + 1, command);
                        if (nextNode.IsEmpty)
                            _nodes.TryRemove(name, out nextNode);
                    }
                }
            }
        }

        public IEnumerable<Command> GetCommands(string text, int index)
        {
            int nextSpace = text.IndexOf(' ', index);
            string name;

            var commands = _commands;
            for (int i = 0; i < commands.Length; i++)
                yield return _commands[i];

            if (text != "")
            {
                if (nextSpace == -1)
                    name = text.Substring(index);
                else
                    name = text.Substring(index, nextSpace - index);

                CommandMapNode nextNode;
                if (_nodes.TryGetValue(name, out nextNode))
                {
                    foreach (var cmd in nextNode.GetCommands(nextSpace == -1 ? "" : text, nextSpace + 1))
                        yield return cmd;
                }
            }
        }
    }
}