using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace AutoDiff
{
    /// <summary>
    /// Represents a custom unary function term. The user provides custom delegates
    /// to evaluate and compute the derivative (differentiate) the function.
    /// </summary>
    public class UnaryFunc : Term
    {
        private readonly Func<double, double> eval;
        private readonly Func<double, double> diff;
        private readonly Term argument;

        /// <summary>
        /// Initializes a new instance of the <see cref="UnaryFunc"/> class.
        /// </summary>
        /// <param name="eval">The evaluation method for the custom function.</param>
        /// <param name="diff">The differentiation method for the custom function.</param>
        /// <param name="argument">The argument term for the unary function</param>
        public UnaryFunc(Func<double, double> eval, Func<double, double> diff, Term argument)
        {
            Contract.Requires(eval != null);
            Contract.Requires(diff != null);
            Contract.Requires(argument != null);

            Contract.Ensures(Eval == eval);
            Contract.Ensures(Diff == diff);
            Contract.Ensures(Argument == argument);

            this.eval = eval;
            this.diff = diff;
            this.argument = argument;
        }

        /// <summary>
        /// Constructs a factory delegate that creates similar unary functions for different terms.
        /// </summary>
        /// <param name="eval">The evaluation method for the custom function.</param>
        /// <param name="diff">The differentiation method for the custom function.</param>
        /// <returns>The described factory delegate</returns>
        public static Func<Term, UnaryFunc> Factory(Func<double, double> eval, Func<double, double> diff)
        {
            Contract.Requires(eval != null);
            Contract.Requires(diff != null);
            Contract.Ensures(Contract.Result<Func<Term, UnaryFunc>>() != null);

            Func<Term, UnaryFunc> result = term => new UnaryFunc(eval, diff, term);
            return result;
        }

        /// <summary>
        /// Gets the evaluation delegate.
        /// </summary>
        public Func<double, double> Eval { get { return eval; } }


        /// <summary>
        /// Gets the differentiation delegate.
        /// </summary>
        public Func<double, double> Diff { get { return diff; } }

        /// <summary>
        /// Gets the function's argument term
        /// </summary>
        public Term Argument { get { return argument; } }

        /// <summary>
        /// Accepts a term visitor
        /// </summary>
        /// <param name="visitor">The term visitor to accept</param>
        public override void Accept(ITermVisitor visitor)
        {
            visitor.Visit(this);
        }

        /// <summary>
        /// Accepts a term visitor with a generic result
        /// </summary>
        /// <typeparam name="TResult">The type of the result from the visitor's function</typeparam>
        /// <param name="visitor">The visitor to accept</param>
        /// <returns>
        /// The result from the visitor's visit function.
        /// </returns>
        public override TResult Accept<TResult>(ITermVisitor<TResult> visitor)
        {
            return visitor.Visit(this);
        }
    }
}