﻿#region License

// Copyright (c) João Palma Sequeira (https://github.com/JMPSequeira)

//

// Licensed under the Apache License, Version 2.0 (the "License");

// you may not use this file except in compliance with the License.

// You may obtain a copy of the License at

//

// http://www.apache.org/licenses/LICENSE-2.0

//

// Unless required by applicable law or agreed to in writing, software

// distributed under the License is distributed on an "AS IS" BASIS,

// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

// See the License for the specific language governing permissions and

// limitations under the License.

//

// The latest version of this file can be found at https://github.com/jeremyskinner/FluentValidation

#endregion License

using System;

namespace JPSoft.CoreValidation
{
    /// <summary>
    /// Class for evaluating every function holding class.
    /// </summary>
    /// <seealso cref="JPSoft.CoreValidation.IEvaluator" />
    internal class Evaluator : IEvaluator
    {
        /// <summary>
        /// Determines whether the specified rule is broken by the current value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="rule">The rule.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if [is rule broken] [the specified rule]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsRuleBroken<T>(IRule<T> rule, T value)
        {
            if (IsConditionValid(rule.When, value) || IsConditionValid(rule.Unless, value))
                if (IsFalse(rule.Condition, value))
                    return true;

            return false;
        }

        /// <summary>
        /// Determines whether the current dependency is met with the current value.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="funcDependency">The function dependency.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if [is dependency met] [the specified function dependency]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsDependencyMet<T>(IFuncDependency<T> funcDependency, T value)
        {
            if (funcDependency.Cadency)
                return IsConditionValid(funcDependency.Func, value);

            return IsConditionInvalid(funcDependency.Func, value);
        }

        /// <summary>
        /// Determines whether the specified condition is valid.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func">The function.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if [is condition valid] [the specified function]; otherwise, <c>false</c>.
        /// </returns>
        private bool IsConditionValid<T>(Func<T, bool> func, T value)
        {
            if (func == null)
                return true;

            if (IsTrue(func, value))
                return true;

            return false;
        }

        /// <summary>
        /// Determines whether the specified condition is invalid.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func">The function.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if [is condition invalid] [the specified function]; otherwise, <c>false</c>.
        /// </returns>
        private bool IsConditionInvalid<T>(Func<T, bool> func, T value)
        {
            if (func == null)
                return false;

            if (IsTrue(func, value))
                return false;

            return true;
        }

        /// <summary>
        /// Determines whether the specified condition is true.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition">The condition.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if the specified condition is true; otherwise, <c>false</c>.
        /// </returns>
        private bool IsTrue<T>(Func<T, bool> condition, T value)
            => TryCatchCondition(condition, value);

        /// <summary>
        /// Determines whether the specified condition is false.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition">The condition.</param>
        /// <param name="value">The value.</param>
        /// <returns>
        ///   <c>true</c> if the specified condition is false; otherwise, <c>false</c>.
        /// </returns>
        private bool IsFalse<T>(Func<T, bool> condition, T value)
            => !TryCatchCondition(condition, value);

        /// <summary>
        /// Tries and catchs condition.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="condition">The condition.</param>
        /// <param name="value">The value.</param>
        /// <returns></returns>
        private bool TryCatchCondition<T>(Func<T, bool> condition, T value)
        {
            try
            {
                return condition(value);
            }
            catch (Exception)
            {
                return false;
            }
        }
    }
}