using System.Linq;

using Hydra.Framework;
using Hydra.Framework.Parsers;
using Hydra.Framework.Parsers.Utility;

namespace Hydra.Framework.CommandLineParser
{
    /// <summary>
    /// Command line Parser
    /// </summary>
    /// <typeparam name="TInput"></typeparam>
    public abstract class TextCommandLineParser<TInput>
        : AbstractCharacterParser<TInput>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TextCommandLineParser&lt;TInput&gt;"/> class.
        /// </summary>
        protected TextCommandLineParser()
        {
            Whitespace = Rep(Char(' ').Or(Char('\t').Or(Char('\n')).Or(Char('\r'))));
            NewLine = Rep(Char('\r').Or(Char('\n')));

            EscChar = (from bs in Char('\\')
                       from ch in Char('\\').Or(Char('\"')).Or(Char('-')).Or(Char('/')).Or(Char('\''))
                       select ch)
                       .Or(from ch in Char(x => x != '"') select ch);

            Id = from w in Whitespace
                 from c in Char(char.IsLetter)
                 from cs in Rep(Char(char.IsLetterOrDigit))
                 select cs.Aggregate(c.ToString(), (s, ch) => s + ch);

            Argument = (from c in Char('a') select (ICommandLineElement)new SwitchElement(c))
                .Or(from c in Char('b') select (ICommandLineElement)new SwitchElement(c));

            Definition = (from w in Whitespace
                          from c in Char('-').Or(Char('/'))
                          from key in Id
                          from eq in Char(':').Or(Char('='))
                          from value in Value
                          select (ICommandLineElement)new DefinitionElement(key, value))
                .Or(from w in Whitespace
                    from c in Char('-').Or(Char('/'))
                    from key in Id
                    from ws in Whitespace
                    from oq in Char('"')
                    from value in Rep(EscChar)
                    from cq in Char('"')
                    select (ICommandLineElement)new DefinitionElement(key, value.Aggregate("", (s, ch) => s + ch)));

            Value = (from symbol in Rep(Char(char.IsLetterOrDigit).Or(Char(char.IsPunctuation))) select symbol.Aggregate("", (s, ch) => s + ch));

            Switch = from w in Whitespace
                     from c in Char('-').Or(Char('/'))
                     from arg in Char(char.IsLetterOrDigit)
                     select (ICommandLineElement)new SwitchElement(arg);

            Token = from w in Whitespace
                    from o in Char('[')
                    from t in Id
                    from c in Char(']')
                    select t;

            Element = (from id in Id select (ICommandLineElement)new ArgumentElement(id))
                .Or(from token in Token select (ICommandLineElement)new TokenElement(token))
                .Or(from element in Definition select element)
                .Or(from element in Switch select element);


            All = from t in Element select t;
        }
        #endregion

        #region Member Variables
        /// <summary>
        /// Gets or sets the definition.
        /// </summary>
        /// <value>The definition.</value>
        public Parser<TInput, ICommandLineElement> Definition
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the esc char.
        /// </summary>
        /// <value>The esc char.</value>
        public Parser<TInput, char> EscChar
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the id.
        /// </summary>
        /// <value>The id.</value>
        public Parser<TInput, string> Id
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the token.
        /// </summary>
        /// <value>The token.</value>
        public Parser<TInput, string> Token
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the value.
        /// </summary>
        /// <value>The value.</value>
        public Parser<TInput, string> Value
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the switch.
        /// </summary>
        /// <value>The switch.</value>
        public Parser<TInput, ICommandLineElement> Switch
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the whitespace.
        /// </summary>
        /// <value>The whitespace.</value>
        public Parser<TInput, char[]> Whitespace
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the new line.
        /// </summary>
        /// <value>The new line.</value>
        public Parser<TInput, char[]> NewLine
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the argument.
        /// </summary>
        /// <value>The argument.</value>
        public Parser<TInput, ICommandLineElement> Argument
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets the element.
        /// </summary>
        /// <value>The element.</value>
        public Parser<TInput, ICommandLineElement> Element
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets or sets all.
        /// </summary>
        /// <value>All.</value>
        public Parser<TInput, ICommandLineElement> All
        {
            get;
            private set;
        }
        #endregion
    }
}