// ========================================================================
//
// Copyright (C) 2016-2017 MARC CHEVALDONNE
//                         marc.chevaldonne.free.fr
//
// Module        : Program.cs
// Author        : Marc Chevaldonné
// Creation date : 2016-10-08
//
// ========================================================================

using System;
using static System.Console;
using System.Linq;

namespace ex_028_013_Predicate
{
    /// <summary>
    /// .NET contient déjà un certain nombre de types délégués dans l'espace de noms System.
    /// Parmi eux, on peut distinguer notamment la famille des Predicate, des Func et des Action.
    /// Cet exemple présente le délégué Predicate.
    /// 
    /// Le délégué Predicate prend un objet générique en paramètre et rend un booléen (true ou false).
    /// N'importe quelle instance d'un Predicate<T> est donc une méthode prenant un T en paramètre et rendant un bool.
    /// </summary>
    class Program
    {
        /// <summary>
        /// Cette méthode vérifie si un nombre entier vérifie une condition
        /// </summary>
        /// <param name="i"></param>
        /// <param name="predicat">la condition à vérifier</param>
        /// <returns></returns>
        static bool CheckIfValid(int i, Predicate<int> predicat)
        {
            return predicat(i);
        }

        /// <summary>
        /// Cette méthode vérifie si tous les nombres entiers du tableu tab vérifient une condition
        /// </summary>
        /// <param name="tab"></param>
        /// <param name="predicat">la condition à vérifier</param>
        /// <returns></returns>
        static bool CheckIfValid(int[] tab, Predicate<int> predicat)
        {
            bool result = true;
            foreach (int i in tab)
            {
                result &= predicat(i);
            }
            return result;
        }

        /// <summary>
        /// Cette méthode prend un entier et rend un bool, 
        /// c'est donc une instance de Predicate<int>
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        static bool IsPair(int i)
        {
            return i % 2 == 0;
        }

        /// <summary>
        /// Cette méthode prend un entier et rend un bool, 
        /// c'est donc une instance de Predicate<int>
        /// </summary>
        /// <param name="i"></param>
        /// <returns></returns>
        static bool IsHigherThan5(int i)
        {
            return i > 5;
        }

        static void Main(string[] args)
        {
            WriteLine("CheckIfValid(4, IsPair) : ");
            WriteLine(CheckIfValid(4, IsPair));
            WriteLine("CheckIfValid(7, IsPair) : ");
            WriteLine(CheckIfValid(7, IsPair));

            WriteLine("CheckIfValid(4, IsHigherThan5) : ");
            WriteLine(CheckIfValid(4, IsHigherThan5));
            WriteLine("CheckIfValid(7, IsHigherThan5) : ");
            WriteLine(CheckIfValid(7, IsHigherThan5));

            WriteLine("CheckIfValid(new int[]{ 2, 4, 6, 8, 10}, IsPair) : ");
            WriteLine(CheckIfValid(new int[] { 2, 4, 6, 8, 10 }, IsPair));
            WriteLine("CheckIfValid(new int[]{ 2, 4, 7, 8, 10}, IsPair) : ");
            WriteLine(CheckIfValid(new int[] { 2, 4, 7, 8, 10 }, IsPair));

            WriteLine("CheckIfValid(new int[] { 2, 4, 6, 8, 10 }, IsHigherThan5) : ");
            WriteLine(CheckIfValid(new int[] { 2, 4, 6, 8, 10 }, IsHigherThan5));
            WriteLine("CheckIfValid(new int[] { 7, 8, 10 }, IsHigherThan5) : ");
            WriteLine(CheckIfValid(new int[] { 7, 8, 10 }, IsHigherThan5));

            //les Predicate se marient très bien avec les expressions lambda (cf. chap 30)
            //qui permettent d'éviter de définir les méthodes 
            WriteLine("CheckIfValid(4, i => i%2==0) : ");
            WriteLine(CheckIfValid(4, i => i % 2 == 0));
            WriteLine("CheckIfValid(7, i => i>5) : ");
            WriteLine(CheckIfValid(7, i => i > 5));

            //elles se marient également parfaitement avec LINQ (cf. chap31)
            //Where par exemple prend un paramètre de type Predicate<T>
            //Voici un exemple avec une projection de Predicate<T> et une aggregation qui déchire
            WriteLine("CheckIfAllConditionsAreValid(2, i => i % 2 == 0, i => i > 5) : ");
            WriteLine(CheckIfAllConditionsAreValid(2, i => i % 2 == 0, i => i > 5));
            WriteLine("CheckIfAllConditionsAreValid(6, i => i % 2 == 0, i => i > 5) : ");
            WriteLine(CheckIfAllConditionsAreValid(6, i => i % 2 == 0, i => i > 5));
        }

        static bool CheckIfAllConditionsAreValid(int i, params Predicate<int>[] predicats)
        {
            return predicats.Select(p => p(i)).Aggregate(true, (result, b) => result &= b);
        }
    }
}
