// ConsoleTools
// Copyright (C) 2017-2024 Dust in the Wind
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;

namespace DustInTheWind.ConsoleTools.Demo.NetCore.ProgressBarDemo.BusinessLayer;

/// <summary>
/// Base class that asynchrounously runs some code and provides support to announce its progress.
/// </summary>
internal abstract class JobBase
{
    private JobState state = JobState.New;
    private readonly ManualResetEventSlim finishEvent;

    protected JobBase()
    {
        finishEvent = new ManualResetEventSlim(false);
    }

    /// <summary>
    /// Gets a galue that specifies if the job is running.
    /// </summary>
    public JobState State
    {
        get => state;
        private set
        {
            state = value;
            OnStateChanged();
        }
    }

    /// <summary>
    /// Event raised when the state of the jog is changed.
    /// </summary>
    public event EventHandler StateChanged;

    /// <summary>
    /// Event raised when the progress of the job changed.
    /// </summary>
    public event EventHandler<ProgressChangedEventArgs> ProgressChanged;

    /// <summary>
    /// Runs the <see cref="DoRun"/> method asynchronously.
    /// The <see cref="Start"/> method immediatelly return and the job remains to be run on a background thread.
    /// </summary>
    public void Start()
    {
        finishEvent.Reset();
        State = JobState.Running;

        Task.Run(() =>
        {
            try
            {
                DoRun();
            }
            finally
            {
                finishEvent.Set();
                State = JobState.Stopped;
            }
        });
    }

    /// <summary>
    /// Runs the <see cref="DoRun"/> method synchronously.
    /// The <see cref="Run"/> methods is blocked until the job is finished.
    /// </summary>
    public void Run()
    {
        State = JobState.Running;

        try
        {
            DoRun();
        }
        finally
        {
            State = JobState.Stopped;
        }
    }

    protected abstract void DoRun();

    protected void AnnounceProgress(int progressPercentage)
    {
        ProgressChangedEventArgs eventArgs = new(progressPercentage, null);
        OnProgressChanged(eventArgs);
    }

    public void WaitToFinish()
    {
        finishEvent.Wait();
    }

    protected virtual void OnStateChanged()
    {
        StateChanged?.Invoke(this, EventArgs.Empty);
    }

    protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
    {
        ProgressChanged?.Invoke(this, e);
    }
}