﻿// ----------------------------------------------------
// Copyright (c) Andrew Lopreiato. All rights reserved.
// ----------------------------------------------------
using System;
using System.Collections.Generic;

namespace Bullfrog
{
    /// <summary>
    /// The token that determines what logic to apply in the condition.
    /// </summary>
    [Serializable]
    public class FunctionToken
    {
        /// <summary>
        /// The function being called on the operands.
        /// </summary>
        /// <exception cref="ArgumentNullException">Upon being set to null</exception>
        public string Text
        {
            get
            {
                return text;
            }
            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }
                text = value;
            }
        }
        private string text;

        /// <summary>
        /// The function that this operation represents.
        /// </summary>
        public OperationFunction Invoke
        {
            get
            {
                OperationFunction retVal;
                if (!OperationManifest.TryGetValue(Text, out retVal))
                {
                    throw new NotImplementedException(
                        string.Format("Function \"{0}\" not registered in the manifest.", Text));
                }
                return retVal;
            }
        }

        /// <param name="text">The operation from the manifest to apply</param>
        /// <exception cref="ArgumentNullException">Upon being set to null</exception>
        public FunctionToken(string text)
        {
            this.Text = text;
        }

        /// <summary>
        /// Performs some logic on two operands with a given context.
        /// </summary>
        public delegate bool OperationFunction(Dictionary<string, string> context, ParameterToken[] parameters);

        /// <summary>
        /// All of the known functions conditions can perform.
        /// </summary>
        [NonSerialized]
        public static Dictionary<string, OperationFunction> OperationManifest = new Dictionary<string, OperationFunction>()
        {
            {"ContextAllExist", ContextAllExist},
            {"ContextOneExists", ContextOneExists },
            { "StringsAllEqualCi", StringsAllEqualCi }
        };

        // Syntax: ContextAllExist(%string%,...)
        private static bool ContextAllExist(Dictionary<string, string> context, ParameterToken[] parameters)
        {
            foreach (ParameterToken parameter in parameters)
            {
                if (!context.ContainsKey(parameters[0].GetValue(context, "")))
                {
                    return false;
                }
            }
            return true;
        }

        // Syntax: ContextOneExists(%string%,...)
        private static bool ContextOneExists(Dictionary<string, string> context, ParameterToken[] parameters)
        {
            foreach (ParameterToken parameter in parameters)
            {
                if (context.ContainsKey(parameters[0].GetValue(context, "")))
                {
                    return true;
                }
            }
            return false;
        }

        // Syntax: StringsAllEqualCi(%string%,...)
        private static bool StringsAllEqualCi(Dictionary<string, string> context, ParameterToken[] parameters)
        {
            if (parameters.Length < 2)
            {
                return false;
            }
            string first = parameters[0].GetValue(context, null);
            if (first == null)
            {
                return false;
            }
            first = first.ToLowerInvariant();
            foreach (ParameterToken parameter in parameters)
            {
                string parameterValue = parameter.GetValue(context, null);
                if (parameterValue == null)
                {
                    return false;
                }
                if (!parameterValue.ToLowerInvariant().Equals(first))
                {
                    return false;
                }
            }
            return true;
        }
    }
}
