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

using System.Linq;
using static System.Console;

namespace ex_016_006_local_functions
{
    /// <summary>
    /// Pour plus de lisibilité, on ajoute régulièrement des méthodes privées à nos classes pour permettre de décomposer des algorithmes plus complexes, réaliser une récursivité
    /// ou encore appeler plusieurs fois un même morceau d'algorithme (dans des tests par exemple).
    /// Parfois, ces méthodes privées ne sont appelées que dans une seule méthode, et alourdisse la classe d'une méthode de plus, ce qui diminue un peu la visibilité des autres méthodes.
    /// 
    /// Depuis C# 7.0, il est maintenant possible de définir des méthodes privées à l'intérieur d'autres méthodes. Le compilateur les traitera comme d'autres méthodes privées dont la 
    /// visibilité serait limitée à la méthode dans laquelle elle est définie. 
    /// Ceci pouvait déjà être fait avec des délégués, à quelques détails près, mais cette forme peut parfois être plus lisible et permet d'utiliser les paramètres ref et out.
    /// 
    /// Dans l'exemple suivant, on réalise une méthode testant si une chaîne de caractères est un palindrome en utilisant une méthode privée
    /// pour tester l'égalité du premier et du dernier caractère. Deux méthodes sont utilisées (sans et avec récursivité).
    /// Ensuite, on utilise une troisième méthode privée au sein du test pour tester plusieurs fois si des chaînes de caractères sont des palindromes en utilisant la méthode précédente.
    /// </summary>

    static class StringUtils
    {
        /// <summary>
        /// méthode testant sur un mot est un palindrome en utilisant une méthode locale
        /// </summary>
        /// <param name="word">mot à tester</param>
        /// <returns></returns>
        public static bool IsPalindrome(string word)
        {
            //teste si le mot est blanc ou nul
            if (string.IsNullOrWhiteSpace(word)) return false;

            bool result = true;

            //utilisation de la méthode locale
            while ((result = CheckBoundaries(ref word)) && word.Length > 2) ;

            return result;

            //définition ici de la méthode locale, imbriquée dans la méthode IsPalindrome
            bool CheckBoundaries(ref string str)
            {
                str = str.Trim();
                if (str[0] != str.Last()) return false;
                str = str.Substring(1, str.Length - 2);
                return true;
            }
        }

        /// <summary>
        /// méthode testant sur un mot est un palindrome en utilisant une méthode locale
        /// </summary>
        /// <param name="word">mot à tester</param>
        /// <returns></returns>
        public static bool IsPalindrome2(string word)
        {
            //teste si le mot est blanc ou nul
            if (string.IsNullOrWhiteSpace(word)) return false;

            //utilisation de la méthode locale
            return CheckBoundaries(word);

            //définition de la méthode locale
            bool CheckBoundaries(string str)
            {
                str = str.Trim();
                if (str[0] != str.Last()) return false;
                str = str.Substring(1, str.Length - 2);
                if (str.Length > 2)
                    return CheckBoundaries(str);
                return true;
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TestIsPalindrome();
        }

        /// <summary>
        /// teste les méthodes IsPalindrome et IsPalindrome2
        /// Comme on réalise plusieurs fois les mêmes actions, on crée une méthode locale pour l'ensemble des tests. 
        /// Pratique dans les tests unitaires notamment.
        /// </summary>
        static void TestIsPalindrome()
        {
            TestAWord("abcdef");
            TestAWord("abcba");
            TestAWord("abccba");

            TestAWord2("abcdef");
            TestAWord2("abcba");
            TestAWord2("abccba");

            void TestAWord(string word)
            {
                WriteLine($"TEST DE LA METHODE IsPalindrome AVEC LE MOT : {word}");
                bool result = StringUtils.IsPalindrome(word);
                if (result)
                {
                    WriteLine($"Le mot {word} est un palindrome");
                }
                else
                {
                    WriteLine($"Le mot {word} n'est pas un palindrome");
                }
                WriteLine();
            }

            void TestAWord2(string word)
            {
                WriteLine($"TEST DE LA METHODE IsPalindrome2 AVEC LE MOT : {word}");
                bool result = StringUtils.IsPalindrome2(word);
                if (result)
                {
                    WriteLine($"Le mot {word} est un palindrome");
                }
                else
                {
                    WriteLine($"Le mot {word} n'est pas un palindrome");
                }
                WriteLine();
            }
        }
    }
}
