using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Quartz;
using System.Web.Script.Serialization;

namespace Disco.Services.Tasks
{
    public class ScheduledTaskStatus
    {
        #region Backing Fields

        private string _sessionId;
        private string _triggerKey;
        private string _taskName;
        private Type _taskType;
        private bool _isSilent;

        private byte _progress;
        private string _currentProcess;
        private string _currentDescription;

        private Exception _taskException;
        private bool _cancelInitiallySupported;
        private bool _cancelSupported;
        private bool _isCanceling;

        private DateTime? _startedTimestamp;
        private DateTime? _nextScheduledTimestamp;
        private DateTime? _finishedTimestamp;

        private string _finishedMessage;
        private string _finishedUrl;

        private int _statusVersion = 0;

        #endregion

        #region Properties

        public string SessionId { get { return this._sessionId; } }
        public string TriggerKey { get { return this._triggerKey; } }
        public string TaskName { get { return this._taskName; } }
        public Type TaskType { get { return this._taskType; } }
        public bool IsSilent { get { return this._isSilent; } }

        public byte Progress { get { return this._progress; } }
        public string CurrentProcess { get { return this._currentProcess; } }
        public string CurrentDescription { get { return this._currentDescription; } }

        public Exception TaskException { get { return this._taskException; } }
        public bool CancelSupported { get { return this._cancelSupported; } }
        public bool IsCanceling { get { return this._isCanceling; } }

        public DateTime? StartedTimestamp { get { return this._startedTimestamp; } }
        public DateTime? FinishedTimestamp { get { return this._finishedTimestamp; } }
        public DateTime? NextScheduledTimestamp { get { return this._nextScheduledTimestamp; } }

        public string FinishedMessage { get { return this._finishedMessage; } }
        public string FinishedUrl { get { return this._finishedUrl; } }

        public int StatusVersion { get { return this._statusVersion; } }

        public bool IsRunning
        {
            get
            {
                return _startedTimestamp.HasValue && !_finishedTimestamp.HasValue;
            }
        }

        #endregion

        #region Events
        public delegate void UpdatedEvent(ScheduledTaskStatus sender, string[] ChangedProperties);
        public delegate void CancelingEvent(ScheduledTaskStatus sender);
        public event UpdatedEvent Updated;
        public event CancelingEvent Canceling;
        #endregion

        public ScheduledTaskStatus(ScheduledTask Task, string SessionId, string TriggerKey, string FinishedUrl = null)
        {
            this._taskName = Task.TaskName;
            this._taskType = Task.GetType();

            this._sessionId = SessionId;
            this._triggerKey = TriggerKey;
            this._cancelInitiallySupported = Task.CancelInitiallySupported;
            this._cancelSupported = this._cancelInitiallySupported;

            this._finishedUrl = FinishedUrl;

            this._currentProcess = "Scheduled";
            this._currentDescription = "Scheduled Task for Execution";

            this._progress = 0;
        }

        #region Progress Actions
        public void UpdateStatus(byte Progress)
        {
            this._progress = Progress;
            UpdateTriggered(new string[] { "Progress" });
        }
        public void UpdateStatus(double Progress)
        {
            UpdateStatus((byte)Progress);
        }
        public void UpdateStatus(string CurrentDescription)
        {
            this._currentDescription = CurrentDescription;
            UpdateTriggered(new string[] { "CurrentDescription" });
        }
        public void UpdateStatus(byte Progress, string CurrentDescription)
        {
            this._progress = Progress;
            this._currentDescription = CurrentDescription;
            UpdateTriggered(new string[] { "Progress", "CurrentDescription" });
        }
        public void UpdateStatus(double Progress, string CurrentDescription)
        {
            UpdateStatus((byte)Progress, CurrentDescription);
        }
        public void UpdateStatus(byte Progress, string CurrentProcess, string CurrentDescription)
        {
            this._progress = Progress;
            this._currentProcess = CurrentProcess;
            this._currentDescription = CurrentDescription;
            UpdateTriggered(new string[] { "Progress", "CurrentProcess", "CurrentDescription" });
        }
        public void UpdateStatus(double Progress, string CurrentProcess, string CurrentDescription)
        {
            UpdateStatus((byte)Progress, CurrentProcess, CurrentDescription);
        }
        #endregion

        #region State Actions
        public bool Canceled()
        {
            if (!this._isCanceling)
            {
                if (_cancelSupported)
                { // Cancelling
                    this._isCanceling = true;
                    UpdateTriggered(new string[] { "IsCancelling" });
                    if (this.Canceling != null)
                        Canceling(this);
                    return true;
                }
                else
                { // Cancelling not supported
                    return false;
                }
            }
            else
            { // Already Cancelling
                return true;
            }
        }
        public void SetCancelSupported(bool CancelSupported)
        {
            if (this._cancelSupported != CancelSupported)
            {
                this._cancelSupported = CancelSupported;
                UpdateTriggered(new string[] { "CancelSupported" });
            }
        }
        public void SetTaskException(Exception TaskException)
        {
            if (this._taskException != TaskException)
            {
                this._taskException = TaskException;
                UpdateTriggered(new string[] { "TaskException" });
            }
        }
        public void SetIsSilent(bool IsSilent)
        {
            if (this._isSilent != IsSilent)
                this._isSilent = IsSilent;
        }
        public void SetFinishedUrl(string FinishedUrl)
        {
            if (this._finishedUrl != FinishedUrl)
            {
                this._finishedUrl = FinishedUrl;
                UpdateTriggered(new string[] { "FinishedUrl" });
            }
        }
        public void SetFinishedMessage(string FinishedMessage)
        {
            if (this._finishedMessage != FinishedMessage)
            {
                this._finishedMessage = FinishedMessage;
                UpdateTriggered(new string[] { "FinishedMessage" });
            }
        }
        public void SetNextScheduledTimestamp(DateTime? NextScheduledTimestamp)
        {
            if (this._nextScheduledTimestamp != NextScheduledTimestamp)
            {
                this._nextScheduledTimestamp = NextScheduledTimestamp;
                UpdateTriggered(new string[] { "NextScheduledTimestamp" });
            }
        }
        public void Started()
        {
            List<string> changedProperties = new List<string>() { "IsRunning", "StartedTimestamp" };

            this._startedTimestamp = DateTime.Now;

            if (this._nextScheduledTimestamp != null)
            {
                this._nextScheduledTimestamp = null;
                changedProperties.Add("NextScheduledTimestamp");
            }
            if (this._finishedTimestamp != null)
            {
                this._finishedTimestamp = null;
                changedProperties.Add("FinishedTimestamp");
            }
            if (this._progress != 0)
            {
                this._progress = 0;
                changedProperties.Add("Progress");
            }
            if (this._currentProcess != "Starting")
            {
                this._currentProcess = "Starting";
                changedProperties.Add("CurrentProcess");
            }
            if (this._currentDescription != "Initializing Task for Execution")
            {
                this._currentDescription = "Initializing Task for Execution";
                changedProperties.Add("CurrentDescription");
            }
            if (this._taskException != null)
            {
                this._taskException = null;
                changedProperties.Add("TaskException");
            }
            if (this._cancelSupported != this._cancelInitiallySupported)
            {
                this._cancelSupported = this._cancelInitiallySupported;
                changedProperties.Add("CancelSupported");
            }
            {
                this._isCanceling = false;
                changedProperties.Add("IsCanceling");
            }
            if (this._isCanceling)
            {
                this._isCanceling = false;
                changedProperties.Add("IsCanceling");
            }
            UpdateTriggered(changedProperties.ToArray());
        }
        public void Finished()
        {
            Finished(this._finishedMessage, this._finishedUrl);
        }
        public void Finished(string FinishedMessage, string FinishedUrl)
        {
            List<string> changedProperties = new List<string>() { "IsRunning", "FinishedTimestamp" };

            this._finishedTimestamp = DateTime.Now;

            if (FinishedMessage != this._finishedMessage)
            {
                this._finishedMessage = FinishedMessage;
                changedProperties.Add("FinishedMessage");
            }
            if (FinishedUrl != this._finishedUrl)
            {
                this._finishedUrl = FinishedUrl;
                changedProperties.Add("FinishedUrl");
            }

            if (this._isCanceling)
            {
                this._isCanceling = false;
                changedProperties.Add("IsCanceling");
            }
            UpdateTriggered(changedProperties.ToArray());
        }
        public void Reset(DateTime? NextScheduledTimestamp)
        {
            List<string> changedProperties = new List<string>();

            if (this._nextScheduledTimestamp != NextScheduledTimestamp)
            {
                this._nextScheduledTimestamp = NextScheduledTimestamp;
                changedProperties.Add("NextScheduledTimestamp");
            }

            if (this._startedTimestamp != null)
            {
                this._startedTimestamp = null;
                changedProperties.Add("StartedTimestamp");
            }
            if (this._finishedTimestamp != null)
            {
                this._finishedTimestamp = null;
                changedProperties.Add("FinishedTimestamp");
            }
            if (this._finishedMessage != null)
            {
                this._finishedMessage = null;
                changedProperties.Add("FinishedMessage");
            }
            if (this._finishedUrl != null)
            {
                this._finishedUrl = null;
                changedProperties.Add("FinishedUrl");
            }
            if (this._progress != 0)
            {
                this._progress = 0;
                changedProperties.Add("Progress");
            }
            if (this._currentProcess != "Scheduled")
            {
                this._currentProcess = "Scheduled";
                changedProperties.Add("CurrentProcess");
            }
            if (this._currentDescription != "Scheduled Task for Execution")
            {
                this._currentDescription = "Scheduled Task for Execution";
                changedProperties.Add("CurrentDescription");
            }
            if (this._isCanceling)
            {
                this._isCanceling = false;
                changedProperties.Add("IsCanceling");
            }
            UpdateTriggered(changedProperties.ToArray());
        }
        #endregion

        private void UpdateTriggered(string[] ChangedProperties)
        {
            this._statusVersion++;

            if (Updated != null)
                Updated(this, ChangedProperties);

            if (!_isSilent)
                ScheduledTasksLiveStatusService.Broadcast(ScheduledTaskStatusLive.FromScheduledTaskStatus(this, ChangedProperties));
        }
    }
}
