using CommonLang.Property;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace CommonServer.Command
{
    public abstract class AbstractCommand
    {
        public abstract string Key { get; }
        public abstract void DoCommand(string arg, TextWriter output);
        public virtual string Help
        {
            get
            {
                DescAttribute desc = PropertyUtil.GetAttribute<DescAttribute>(GetType());
                if (desc != null) { return (desc.Desc); }
                return GetType().Name;
            }
        }

        private static Regex reg = new Regex(@"\s+");
        public static string[] ToArgs(string arg)
        {
            return reg.Split(arg);
        }
    }

    public class CommandList<T> where T : AbstractCommand
    {
        private SortedDictionary<string, T> cmd_list = new SortedDictionary<string, T>();
        private static Regex split = new Regex(@"\s+");

        public T[] CmdList
        {
            get { return cmd_list.Values.ToArray(); }
        }

        public CommandList()
        {
            Type type = typeof(T);
            List<Type> allcmd = ReflectionUtil.GetNoneVirtualSubTypes(type, type.Assembly);
            foreach (Type tcmd in allcmd)
            {
                T cmd = ReflectionUtil.CreateInstance(tcmd) as T;
                if (cmd != null)
                {
                    cmd_list.Add(cmd.Key, cmd);
                }
            }
        }

        public bool DoCommand(string line, TextWriter output)
        {
            foreach (AbstractCommand cmd in cmd_list.Values)
            {
                if (line.ToLower().StartsWith(cmd.Key.ToLower()))
                {
                    var arg = line.Substring(cmd.Key.Length);
                    if (arg.Trim().Length > 0)
                    {
                        var mat = split.Match(arg);
                        if (!mat.Success || mat.Index != 0)
                        {
                            continue;
                        }
                    }
                    try
                    {
                        if (line.EndsWith("/?") || line.EndsWith("/help"))
                        {
                            output.WriteLine(cmd.Help);
                        }
                        else
                        {
                            cmd.DoCommand(arg.Trim(), output);
                        }
                    }
                    catch (Exception err)
                    {
                        output.WriteLine(err.Message);
                    }
                    return true;
                }
            }
            return false;
        }

        public string ListCommand(string prefix)
        {
            List<AbstractCommand> cmdlist = new List<AbstractCommand>();
            SortedDictionary<string, List<AbstractCommand>> catgory_map = new SortedDictionary<string, List<AbstractCommand>>();
            foreach (AbstractCommand cmd in CmdList)
            {
                DescAttribute desc = PropertyUtil.GetAttribute<DescAttribute>(cmd.GetType());
                if (desc == null || string.IsNullOrEmpty(desc.Category))
                {
                    cmdlist.Add(cmd);
                }
                else
                {
                    List<AbstractCommand> catgory;
                    if (!catgory_map.TryGetValue(desc.Category, out catgory))
                    {
                        catgory = new List<AbstractCommand>();
                        catgory_map.Add(desc.Category, catgory);
                    }
                    catgory.Add(cmd);
                }
            }
            StringBuilder sb = new StringBuilder();
            ListCommand_Internal(sb, prefix, cmdlist);
            foreach (KeyValuePair<string, List<AbstractCommand>> catgory in catgory_map)
            {
                sb.AppendLine("[" + catgory.Key + "]");
                ListCommand_Internal(sb, prefix, catgory.Value);
            }
            return sb.ToString();
        }


        private void ListCommand_Internal(StringBuilder sb, string prefix, List<AbstractCommand> cmdlist)
        {
            bool show_prefix = string.IsNullOrEmpty(prefix);
            foreach (AbstractCommand cmd in cmdlist)
            {
                if (show_prefix || cmd.Key.StartsWith(prefix))
                {
                    string line = cmd.Key;
                    DescAttribute desc = PropertyUtil.GetAttribute<DescAttribute>(cmd.GetType());
                    if (desc != null)
                    {
                        line += "\t - " + desc.Desc;
                    }
                    sb.AppendLine(line);
                }
            }
        }
    }
}