using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Server.Commands.Generic
{
    [Flags]
    public enum CommandSupport
    {
        Single = 0x0001,
        Global = 0x0002,
        Online = 0x0004,
        Multi = 0x0008,
        Area = 0x0010,
        Self = 0x0020,
        Region = 0x0040,
        Contained = 0x0080,
        IPAddress = 0x0100,

        All = Single | Global | Online | Multi | Area | Self | Region | Contained | IPAddress,
        AllMobiles = All & ~Contained,
        AllNPCs = All & ~(IPAddress | Online | Self | Contained),
        AllItems = All & ~(IPAddress | Online | Self | Region),

        Simple = Single | Multi,
        Complex = Global | Online | Area | Region | Contained | IPAddress
    }

    public abstract class BaseCommandImplementor
    {
        private static List<BaseCommandImplementor> m_Implementors;
        private readonly Dictionary<string, BaseCommand> m_Commands;
        private string[] m_Accessors;
        private AccessLevel m_AccessLevel;
        private CommandSupport m_SupportRequirement;
        private string m_Usage;
        private string m_Description;
        private bool m_SupportsConditionals;
        public BaseCommandImplementor()
        {
            this.m_Commands = new Dictionary<string, BaseCommand>(StringComparer.OrdinalIgnoreCase);
        }

        public static List<BaseCommandImplementor> Implementors
        {
            get
            {
                if (m_Implementors == null)
                {
                    m_Implementors = new List<BaseCommandImplementor>();
                    RegisterImplementors();
                }

                return m_Implementors;
            }
        }
        public bool SupportsConditionals
        {
            get
            {
                return this.m_SupportsConditionals;
            }
            set
            {
                this.m_SupportsConditionals = value;
            }
        }
        public string[] Accessors
        {
            get
            {
                return this.m_Accessors;
            }
            set
            {
                this.m_Accessors = value;
            }
        }
        public string Usage
        {
            get
            {
                return this.m_Usage;
            }
            set
            {
                this.m_Usage = value;
            }
        }
        public string Description
        {
            get
            {
                return this.m_Description;
            }
            set
            {
                this.m_Description = value;
            }
        }
        public AccessLevel AccessLevel
        {
            get
            {
                return this.m_AccessLevel;
            }
            set
            {
                this.m_AccessLevel = value;
            }
        }
        public CommandSupport SupportRequirement
        {
            get
            {
                return this.m_SupportRequirement;
            }
            set
            {
                this.m_SupportRequirement = value;
            }
        }
        public Dictionary<string, BaseCommand> Commands
        {
            get
            {
                return this.m_Commands;
            }
        }
        public static void RegisterImplementors()
        {
            Register(new RegionCommandImplementor());
            Register(new GlobalCommandImplementor());
            Register(new OnlineCommandImplementor());
            Register(new SingleCommandImplementor());
            Register(new SerialCommandImplementor());
            Register(new MultiCommandImplementor());
            Register(new AreaCommandImplementor());
            Register(new SelfCommandImplementor());
            Register(new ContainedCommandImplementor());
            Register(new IPAddressCommandImplementor());

            Register(new RangeCommandImplementor());
            Register(new ScreenCommandImplementor());
            Register(new FacetCommandImplementor());
        }

        public static void Register(BaseCommandImplementor impl)
        {
            m_Implementors.Add(impl);
            impl.Register();
        }

        public virtual void Compile(Mobile from, BaseCommand command, ref string[] args, ref object obj)
        {
            obj = null;
        }

        public virtual void Register(BaseCommand command)
        {
            for (int i = 0; i < command.Commands.Length; ++i)
                this.m_Commands[command.Commands[i]] = command;
        }

        public bool CheckObjectTypes(Mobile from, BaseCommand command, Extensions ext, out bool items, out bool mobiles)
        {
            items = mobiles = false;

            ObjectConditional cond = ObjectConditional.Empty;

            foreach (BaseExtension check in ext)
            {
                if (check is WhereExtension)
                {
                    cond = (check as WhereExtension).Conditional;

                    break;
                }
            }

            bool condIsItem = cond.IsItem;
            bool condIsMobile = cond.IsMobile;

            switch ( command.ObjectTypes )
            {
                case ObjectTypes.All:
                case ObjectTypes.Both:
                    {
                        if (condIsItem)
                            items = true;

                        if (condIsMobile)
                            mobiles = true;

                        break;
                    }
                case ObjectTypes.Items:
                    {
                        if (condIsItem)
                        {
                            items = true;
                        }
                        else if (condIsMobile)
                        {
                            from.SendMessage("You may not use a mobile type condition for this command.");
                            return false;
                        }

                        break;
                    }
                case ObjectTypes.Mobiles:
                    {
                        if (condIsMobile)
                        {
                            mobiles = true;
                        }
                        else if (condIsItem)
                        {
                            from.SendMessage("You may not use an item type condition for this command.");
                            return false;
                        }

                        break;
                    }
            }

            return true;
        }

        public void RunCommand(Mobile from, BaseCommand command, string[] args)
        {
            try
            {
                object obj = null;

                this.Compile(from, command, ref args, ref obj);

                this.RunCommand(from, obj, command, args);
            }
            catch (Exception ex)
            {
                from.SendMessage(ex.Message);
            }
        }

        public string GenerateArgString(string[] args)
        {
            if (args.Length == 0)
                return "";

            // NOTE: this does not preserve the case where quotation marks are used on a single word

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < args.Length; ++i)
            {
                if (i > 0)
                    sb.Append(' ');

                if (args[i].IndexOf(' ') >= 0)
                {
                    sb.Append('"');
                    sb.Append(args[i]);
                    sb.Append('"');
                }
                else
                {
                    sb.Append(args[i]);
                }
            }

            return sb.ToString();
        }

        public void RunCommand(Mobile from, object obj, BaseCommand command, string[] args)
        {
            //	try
            //	{
            CommandEventArgs e = new CommandEventArgs(from, command.Commands[0], this.GenerateArgString(args), args);

            if (!command.ValidateArgs(this, e))
                return;

            bool flushToLog = false;

            if (obj is ArrayList)
            {
                ArrayList list = (ArrayList)obj;

                if (list.Count > 20)
                    CommandLogging.Enabled = false;
                else if (list.Count == 0)
                    command.LogFailure("Nothing was found to use this command on.");

                command.ExecuteList(e, list);

                if (list.Count > 20)
                {
                    flushToLog = true;
                    CommandLogging.Enabled = true;
                }
            }
            else if (obj != null)
            {
                if (command.ListOptimized)
                {
                    ArrayList list = new ArrayList();
                    list.Add(obj);
                    command.ExecuteList(e, list);
                }
                else
                {
                    command.Execute(e, obj);
                }
            }

            command.Flush(from, flushToLog);
            //	}
            //	catch ( Exception ex )
            //	{
            //		from.SendMessage( ex.Message );
            //	}
        }

        public virtual void Process(Mobile from, BaseCommand command, string[] args)
        {
            this.RunCommand(from, command, args);
        }

        public virtual void Execute(CommandEventArgs e)
        {
            if (e.Length >= 1)
            {
                BaseCommand command = null;
                this.m_Commands.TryGetValue(e.GetString(0), out command);

                if (command == null)
                {
                    e.Mobile.SendMessage("That is either an invalid command name or one that does not support this modifier.");
                }
                else if (e.Mobile.AccessLevel < command.AccessLevel)
                {
                    e.Mobile.SendMessage("You do not have access to that command.");
                }
                else
                {
                    string[] oldArgs = e.Arguments;
                    string[] args = new string[oldArgs.Length - 1];

                    for (int i = 0; i < args.Length; ++i)
                        args[i] = oldArgs[i + 1];

                    this.Process(e.Mobile, command, args);
                }
            }
            else
            {
                e.Mobile.SendMessage("You must supply a command name.");
            }
        }

        public void Register()
        {
            if (this.m_Accessors == null)
                return;

            for (int i = 0; i < this.m_Accessors.Length; ++i)
                CommandSystem.Register(this.m_Accessors[i], this.m_AccessLevel, new CommandEventHandler(Execute));
        }
    }
}