﻿#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;
using System.Collections.Generic;

namespace JPSoft.CoreValidation
{
    internal abstract class Rule<T> : IRuleInternal<T>
    {
        private bool hasFailedLastEvaluation;

        private bool applyOnPass;

        protected readonly IGuardian _guardian;

        public RuleCode RuleCode { get; protected set; }

        public virtual Func<T, bool> Condition { get; }

        public virtual Func<T, bool> When { get; private set; }

        public virtual Func<T, bool> Unless { get; private set; }

        public virtual IList<ValidatorHolder> HeldValidators { get; }

        bool IRuleInternal<T>.HasFailedLastEvaluation => hasFailedLastEvaluation;

        /// <summary>
        /// Occurs when rule passes evaluation.
        /// </summary>
        private event EventHandler<IValidationArgs> OnPass;

        /// <summary>
        /// Occurs when rule fails evaluation.
        /// </summary>
        private event EventHandler<IValidationArgs> OnFail;

        event EventHandler<IValidationArgs> ISendValidationArgs.ValidationArgsSent
        {
            add
            {
                if (applyOnPass)
                    OnPass += value;
                else
                    OnFail += value;
            }

            remove
            {
                if (applyOnPass)
                    OnPass -= value;
                else
                    OnFail -= value;
            }
        }

        protected Rule(Func<T, bool> condition)
        {
            Condition = condition;

            _guardian = new Guardian();

            HeldValidators = new List<ValidatorHolder>();
        }

        /// <summary>
        /// Sets the when condition.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        public virtual void SetWhenCondition(Func<T, bool> predicate)
            => When = predicate;

        /// <summary>
        /// Sets the unless condition.
        /// </summary>
        /// <param name="predicate">The predicate.</param>
        public virtual void SetUnlessCondition(Func<T, bool> predicate)
            => Unless = predicate;

        /// <summary>
        /// Called when has been validated.
        /// </summary>
        /// <param name="e">The Validation Arguments</param>
        void IRuleInternal<T>.OnValidated(IValidationArgs e)
        {
            if (hasFailedLastEvaluation)
                OnFail?.Invoke(this, e);
            else
                OnPass?.Invoke(this, e);
        }

        /// <summary>
        /// Virtual method for receiving an IErrorArgsDictionaryBuilder which scans the rule for specific message arguments.
        /// </summary>
        /// <param name="builder">The builder.</param>
        public virtual void ReceiveErrorArgsBuilder(IErrorArgsDictionaryBuilder builder)
        {
        }

        /// <summary>
        /// Sets the applyOnPass to true or false.
        /// </summary>
        /// <param name="dependencyType">Type of the dependency.</param>
        public void AddDependenciesOn(OnRule dependencyType)
            => applyOnPass = (dependencyType == OnRule.Pass);

        /// <summary>
        /// Receives the evaluator so validation is processed.
        /// </summary>
        /// <param name="evaluator">The evaluator.</param>
        /// <param name="value">The value.</param>
        void IRuleInternal<T>.ReceiveEvaluator(IEvaluator evaluator, T value)
            => hasFailedLastEvaluation = evaluator.IsRuleBroken(this, value);
    }
}