using System;

using Svelto.ECS;

using GamecraftModdingAPI.Utility;

namespace GamecraftModdingAPI.Commands
{
    /// <summary>
    /// Custom Command builder.
    /// </summary>
	public class CommandBuilder
	{
		private string name;

		private string description;

		private short parameterCount;

		private ICustomCommandEngine commandEngine;

		private bool fromExisting = false;

		/// <summary>
        /// Create a new command builder.
        /// </summary>
		public CommandBuilder()
		{
			name = "";
			description = null;
			parameterCount = -1;
		}

        /// <summary>
        /// Create and return a command builder.
        /// </summary>
        /// <returns>The builder.</returns>
		public static CommandBuilder Builder()
		{
			return new CommandBuilder();
		}

        /// <summary>
		/// Create a new command builder.
        /// </summary>
        /// <param name="name">The command name.</param>
		/// <param name="description">The command description (shown in help).</param>
		public CommandBuilder(string name, string description = null)
		{
			this.name = name;
			this.description = description;
			parameterCount = -1;
		}

		/// <summary>
        /// Create and return a command builder.
		/// If name and description are provided, this is equivalent to <code>Builder().Name(name).Description(description)</code>
        /// </summary>
		/// <param name="name">The command name.</param>
        /// <param name="description">The command description (shown in help).</param>
        /// <returns>The builder.</returns>
		public static CommandBuilder Builder(string name, string description = null)
        {
			return new CommandBuilder(name, description);
        }

        /// <summary>
        /// Name the command.
        /// </summary>
        /// <returns>The builder.</returns>
        /// <param name="name">The command name.</param>
		public CommandBuilder Name(string name)
		{
			this.name = name;
			return this;
		}

        /// <summary>
        /// Describe the command.
        /// </summary>
        /// <returns>The builder.</returns>
        /// <param name="description">The command description (shown in help).</param>
		public CommandBuilder Description(string description)
		{
			this.description = description;
			return this;
		}

        /// <summary>
        /// Set the action the command performs.
        /// </summary>
		/// <returns>The builder.</returns>
        /// <param name="action">The action to perform when the command is called.</param>
		public CommandBuilder Action(Action action)
		{
			parameterCount = 0;
			commandEngine = new SimpleCustomCommandEngine(action, name, description);
			return this;
		}

		/// <summary>
        /// Set the action the command performs.
        /// </summary>
        /// <returns>The builder.</returns>
        /// <param name="action">The action to perform when the command is called.</param>
		/// <typeparam name="A">The 1st parameter's type.</typeparam>
		public CommandBuilder Action<A>(Action<A> action)
        {
			parameterCount = 1;
            commandEngine = new SimpleCustomCommandEngine<A>(action, name, description);
            return this;
        }

		/// <summary>
        /// Set the action the command performs.
        /// </summary>
        /// <returns>The builder.</returns>
        /// <param name="action">The action to perform when the command is called.</param>
        /// <typeparam name="A">The 1st parameter's type.</typeparam>
		/// <typeparam name="B">The 2nd parameter's type.</typeparam>
		public CommandBuilder Action<A,B>(Action<A,B> action)
        {
			parameterCount = 2;
            commandEngine = new SimpleCustomCommandEngine<A,B>(action, name, description);
            return this;
        }

		/// <summary>
        /// Set the action the command performs.
        /// </summary>
        /// <returns>The builder.</returns>
        /// <param name="action">The action to perform when the command is called.</param>
		/// <typeparam name="A">The 1st parameter's type.</typeparam>
		/// <typeparam name="B">The 2nd parameter's type.</typeparam>
		/// <typeparam name="C">The 3rd parameter's type.</typeparam>
		public CommandBuilder Action<A,B,C>(Action<A,B,C> action)
        {
			parameterCount = 3;
            commandEngine = new SimpleCustomCommandEngine<A,B,C>(action, name, description);
            return this;
        }

        /// <summary>
        /// Build the command from an existing command.
        /// </summary>
        /// <returns>The command. Use Invoke() to execute it.</returns>
		public SimpleCustomCommandEngine FromExisting()
		{
			if (string.IsNullOrWhiteSpace(name))
            {
				throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
            }
			if (!ExistingCommands.Exists(name))
			{
				throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
			}
			fromExisting = true;
			return new SimpleCustomCommandEngine(
				() => { ExistingCommands.Call(name); },
				name,
				description);
		}

		/// <summary>
        /// Build the command from an existing command.
        /// </summary>
        /// <returns>The command. Use Invoke() to execute it.</returns>
        /// <typeparam name="A">The 1st parameter's type.</typeparam>
		public SimpleCustomCommandEngine<A> FromExisting<A>()
        {
            if (string.IsNullOrWhiteSpace(name))
            {
				throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
            }
			if (!ExistingCommands.Exists(name))
            {
				throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
            }
			fromExisting = true;
            return new SimpleCustomCommandEngine<A>(
                (A a) => { ExistingCommands.Call<A>(name, a); },
                name,
                description);
        }

		/// <summary>
        /// Build the command from an existing command.
        /// </summary>
        /// <returns>The command. Use Invoke() to execute it.</returns>
        /// <typeparam name="A">The 1st parameter's type.</typeparam>
        /// <typeparam name="B">The 2nd parameter's type.</typeparam>
		public SimpleCustomCommandEngine<A,B> FromExisting<A,B>()
        {
            if (string.IsNullOrWhiteSpace(name))
            {
				throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
            }
			if (!ExistingCommands.Exists(name))
            {
				throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
            }
			fromExisting = true;
            return new SimpleCustomCommandEngine<A,B>(
                (A a, B b) => { ExistingCommands.Call<A,B>(name, a, b); },
                name,
                description);
        }

		/// <summary>
        /// Build the command from an existing command.
        /// </summary>
        /// <returns>The command. Use Invoke() to execute it.</returns>
        /// <typeparam name="A">The 1st parameter's type.</typeparam>
		/// <typeparam name="B">The 2nd parameter's type.</typeparam>
		/// <typeparam name="C">The 3rd parameter's type.</typeparam>
		public SimpleCustomCommandEngine<A,B,C> FromExisting<A,B,C>()
        {
            if (string.IsNullOrWhiteSpace(name))
            {
				throw new CommandParameterMissingException("Command name must be defined before FromExisting() is called");
            }
			if (!ExistingCommands.Exists(name))
            {
				throw new CommandNotFoundException("Command cannot be built from existing because it does not exist.");
            }
			fromExisting = true;
            return new SimpleCustomCommandEngine<A,B,C>(
                (A a, B b, C c) => { ExistingCommands.Call<A,B,C>(name, a, b, c); },
                name,
                description);
        }

        /// <summary>
        /// Build the command.
        /// </summary>
        /// <returns>The built command.</returns>
        /// <param name="register">Automatically register the command with CommandManager.AddCommand()?</param>
		public ICustomCommandEngine Build(bool register = true)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new CommandParameterMissingException("Command name must be defined before Build() is called");
			}
			if (fromExisting)
			{
				throw new CommandAlreadyBuiltException("Command was already built by FromExisting()");
			}
			if (commandEngine == null)
			{
				throw new CommandParameterMissingException("Command action must be defined before Build() is called");
			}
			if (string.IsNullOrWhiteSpace(description))
			{
				Logging.LogWarning($"Command {FullName()} was built without a description");
			}
			if (register)
			{
				CommandManager.AddCommand(commandEngine);
				Logging.MetaDebugLog($"Command {FullName()} was automatically registered");
			}
			return commandEngine;
		}

        /// <summary>
		/// Get the full command name, in the form [name]::[description]::[# of parameters]
        /// </summary>
        /// <returns>The name.</returns>
        public string FullName()
		{
			if (string.IsNullOrWhiteSpace(description))
			{
				return name + "::" + parameterCount;
			}
			return name + "::" + description + "::" + parameterCount;
		}
	}
}