using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;

namespace ex_050_010_Threading_en_WPF
{
    /// <summary>
    /// Logique d'interaction pour MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //contexte de synchronisation
        //utile que pour le 3ème exemple
        SynchronizationContext mSynchroContext;

        public MainWindow()
        {
            InitializeComponent();
            
            //utile que pour le 3ème exemple
            //récypère le contexte de synchronisation du UI thread
            mSynchroContext = SynchronizationContext.Current;
        }

        ////////////////////////////////////////////
        /* MAUVAISE METHODE : BLOQUE LE UI THREAD */
        ////////////////////////////////////////////

        //simulation de travail
        //met à jour de 10% la progressBar toutes les demi-secondes
        void Travaille(ProgressBar pb)
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(500);
                pb.Value++;
            }
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            //lance la simulation de travail dans le UI thread (thread principal de l'application, ayant accès aux contrôles)
            Travaille(mProgressBar);
            //donne le même résultat ici, si on appelle Travaille2 à la place
        }

        /////////////////////////////////////////////////////////////////
        /* BONNE METHODE N°1 : MARSHALING AVEC DISPATCHER DU UI THREAD */
        /////////////////////////////////////////////////////////////////

        //simulation de travail
        //met à jour de 10% la progressBar toutes les demi-secondes
        void Travaille2(ProgressBar pb)
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(500);
                Action action = () => pb.Value++;
                //permet d'envoyer la requête de modification d'une mise à jour de l'UI sur le UI Thread
                //Cette opération s'appelle : marshaling
                Dispatcher.BeginInvoke(action);
                //On peut aussi utiliser Dispatcher.Invoke qui fait la même chose, mais bloque le thread dans l'attente d'une réponse
                //du UI thread. Si aucune valeur de retour n'est nécessaire, il vaut mieux utiliser BeginInvoke.
            }
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            //lance la simulation de travail dans un "worker" thread
            new Thread(() => Travaille2(mProgressBar2)).Start();
        }

        /////////////////////////////////////////////////////////////////////
        /* BONNE METHODE N°2 : MARSHALING AVEC CONTEXTE DE SYNCHRONISATION */
        /////////////////////////////////////////////////////////////////////

        //simulation de travail
        //met à jour de 10% la progressBar toutes les demi-secondes
        void Travaille3(ProgressBar pb)
        {
            for (int i = 0; i < 10; i++)
            {
                Thread.Sleep(500);
                //permet d'envoyer la requête de modification d'une mise à jour de l'UI sur le UI Thread
                //Cette opération s'appelle : marshaling
                mSynchroContext.Post( _ => pb.Value++, null);
                //Post remplace BeginInvoke
                //Send remplace Invoke
            }
        }

        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            new Thread(() => Travaille3(mProgressBar3)).Start();
        }
    }
}
