using System;
using System.Linq;
using System.Runtime.Versioning;
using System.ServiceProcess;
using System.Threading;


namespace KarleyServiceLibrary
{
    [SupportedOSPlatform("windows")]
    public class ServiceHandler
    {
        public delegate void ControllerStatusChangedHandler(object sender, ControllerStatusChangedEventArgs e);
        public event ControllerStatusChangedHandler? ControllerStatusChanged;
        private CancellationTokenSource _cancellationToken;
        #region Public

        /// <summary>
        /// Der Thread in dem die Überwachung läuft
        /// </summary>
        public Thread WatchThread { get; }

        /// <summary>
        /// Der Controller der den Service verwaltet
        /// </summary>
        public ServiceController ServiceController { get; }

        /// <summary>
        /// Der Name des Service
        /// </summary>
        public string ServiceName { get; }

        /// <summary>
        /// Gibt zurück ob der Service installiert ist oder nicht
        /// </summary>
        public bool IsServiceInstalled => ServiceController.GetServices().Any(x => x.ServiceName.Equals(this.ServiceName));
        #endregion

        #region Private
        /// <summary>
        /// Legt fest ob der Watcher laufen soll oder nicht
        /// </summary>
        private bool RunWatcher { get; set; } = true;

        /// <summary>
        /// Der letzte Status des Service
        /// </summary>
        private ServiceControllerStatus OldStatus { get; set; }
        #endregion

        /// <summary>
        /// Initilaisiert einen neuen ServiceHandler der den Service mit dem gegebenen Namen überwacht
        /// </summary>
        /// <param name="name">Der Name des zu überwachenden Service</param>
        public ServiceHandler(string name)
        {
            this.ServiceName = name;
            this.ServiceController = new ServiceController(this.ServiceName);
            _cancellationToken = new CancellationTokenSource();

            this.WatchThread = new Thread(Watch)
            {
                IsBackground = true,
                Name = $"ServiceWatcher_{this.ServiceName}"
            };

            this.OldStatus = this.ServiceController.Status;
        }

        /// <summary>
        /// Löst das ControllerStatusChanged Eventa aus
        /// </summary>
        /// <param name="oldStatus">Der Alte Status des Service</param>
        /// <param name="newStatus">Der Neue / Aktuelle Status des Service</param>
        private void TriggerControllerStatusChanged(ServiceControllerStatus oldStatus, ServiceControllerStatus newStatus)
        {
            if (ControllerStatusChanged == null)
            {
                return;
            }

            ControllerStatusChangedEventArgs args = new ControllerStatusChangedEventArgs(oldStatus, newStatus);
            ControllerStatusChanged(this, args);
        }

        /// <summary>
        /// Startet die Überwachung des Service Status
        /// </summary>
        public void StartWatcher()
        {
            this.RunWatcher = true;
            this.WatchThread.Start();
        }

        /// <summary>
        /// Stoppt die Überwachung des Service
        /// </summary>
        public void StopWatcher()
        {
            this.RunWatcher = false;
        }

        /// <summary>
        /// Die Funktion die im WatchThread läuft und die Events Triggered
        /// </summary>
        private void Watch()
        {
            while (RunWatcher && !_cancellationToken.IsCancellationRequested)
            {
                this.ServiceController.Refresh();

                if (this.IsServiceInstalled)
                {
                    ServiceControllerStatus tmpStatus = this.ServiceController.Status;
                    if (tmpStatus != OldStatus)
                    {
                        TriggerControllerStatusChanged(OldStatus, tmpStatus);
                        OldStatus = tmpStatus;
                        Thread.Sleep(100);
                    }
                }
            }
        }

        /// <summary>
        /// Startet den Service sofern er nicht schon gestartet ist
        /// </summary>
        public void StartService()
        {
            ServiceControllerStatus status = this.ServiceController.Status;
            if (status != ServiceControllerStatus.Running && status != ServiceControllerStatus.ContinuePending && status != ServiceControllerStatus.StartPending)
            {
                this.ServiceController.Start();
            }
        }

        /// <summary>
        /// Stoppt den Service sofern er noch nicht gestoppt ist
        /// </summary>
        public void StopService()
        {
            ServiceControllerStatus status = this.ServiceController.Status;
            if (status != ServiceControllerStatus.Stopped && status != ServiceControllerStatus.StopPending)
            {
                this.ServiceController.Stop();
            }
        }

        /// <summary>
        /// Startet den Service neu
        /// </summary>
        public void RestartService()
        {
            this.StopService();
            //Direktes Starten und Stoppen verursacht einen Fehler
            Thread.Sleep(100);
            this.StartService();
        }

        /// <summary>
        /// Holt sich den aktuellen Status des Service
        /// </summary>
        /// <returns></returns>
        public ServiceControllerStatus GetStatus()
        {
            this.ServiceController.Refresh();
            return this.ServiceController.Status;
        }

        ~ServiceHandler()
        {
            this.RunWatcher = false;
            _cancellationToken.Cancel();
        }
    }

    public class ControllerStatusChangedEventArgs : EventArgs
    {
        public ServiceControllerStatus OldStatus { get; set; }
        public ServiceControllerStatus NewStatus { get; set; }
        public ControllerStatusChangedEventArgs(ServiceControllerStatus oldStatus, ServiceControllerStatus newStatus)
        {
            this.OldStatus = oldStatus;
            this.NewStatus = newStatus;
        }
    }


}
