/***************************************************************************
 *  AsyncGroupStatusManager.cs
 *
 *  Copyright (C) 2007 Michael C. Urbanski
 *  Written by Mike Urbanski <michael.c.urbanski@gmail.com>
 ****************************************************************************/

/*  THIS FILE IS LICENSED UNDER THE MIT LICENSE AS OUTLINED IMMEDIATELY BELOW: 
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),  
 *  to deal in the Software without restriction, including without limitation  
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,  
 *  and/or sell copies of the Software, and to permit persons to whom the  
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE.
 */
 
 using System;
 using System.Threading;
 
namespace Migo.TaskCore
 {
    public class GroupStatusManager : IDisposable
    {            
        private bool disposed;        
        private bool suspendUpdate;

        private int runningTasks;        
        private int completedTasks;
        private int remainingTasks;
        private int maxRunningTasks = 0;

        private ManualResetEvent mre;     

        public event EventHandler<GroupStatusChangedEventArgs> StatusChanged;

        public virtual int CompletedTasks
        {
            get {
                CheckDisposed ();
                return completedTasks; 
            }
        }        
        
        public virtual int RunningTasks
        {
            get { 
                CheckDisposed ();
                return runningTasks;
            }
        }
        
        public virtual bool SuspendUpdate
        {
            get { 
                CheckDisposed ();
                return suspendUpdate;
            }
            
            set {
                CheckDisposed ();
                suspendUpdate = value; 
            }
        }
        
        public virtual int RemainingTasks
        {
            get { 
                CheckDisposed ();
                return remainingTasks;
            }
            
            set { 
                CheckDisposed ();
                SetRemainingTasks (value);
            }
        }        
        
        public virtual int MaxRunningTasks
        {
            get { 
                CheckDisposed ();
                return maxRunningTasks;
            }
            
            set {
                CheckDisposed ();
                SetMaxRunningTasks (value); 
            }
        }

        public virtual WaitHandle Handle {
            get {
                CheckDisposed ();
                return mre; 
            }
        }

        public GroupStatusManager () : this (0, 0) {}        
        public GroupStatusManager (int totalTasks) : this (totalTasks, 0) {}        
        public GroupStatusManager (int maxRunningTasks, int totalTasks)
        {
            mre = new ManualResetEvent (false);
            
            SetRemainingTasks (totalTasks);
            SetMaxRunningTasks (maxRunningTasks);
        }

        public virtual void Dispose ()
        {
            if (!disposed) {                            
                disposed = true;
                
                if (mre != null) {
                    mre.Close ();
                    mre = null;
                }
            }
        }

        public virtual int IncrementCompletedTaskCount ()
        {
            CheckDisposed ();

            ++completedTasks;
            OnStatusChanged ();   
            
            return completedTasks;
        }

        public virtual int DecrementCompletedTaskCount ()
        {          
            CheckDisposed ();
            
            if (completedTasks == 0) {
               throw new InvalidOperationException (
                   "Completed task count cannot be less than 0"
                );
            }
            
            --completedTasks;
            OnStatusChanged ();                    
            
            return completedTasks;
        }        
        
        public virtual int IncrementRunningTaskCount ()
        {
            CheckDisposed ();
            
            if (runningTasks >= remainingTasks) {
                throw new InvalidOperationException (
                    "Running task count cannot be > remaining task count"
                );
            }        
        
            ++runningTasks;
            OnStatusChanged ();      

            Evaluate ();
            
            return runningTasks;
        }

        public virtual int DecrementRunningTaskCount ()
        {
            CheckDisposed ();
            
            if (runningTasks == 0) {
               throw new InvalidOperationException (
                   "Runing task count cannot be less than 0"
                );
            }
            
            --runningTasks;
            OnStatusChanged ();
            Evaluate ();
            
            return runningTasks;
        }

        public virtual int IncrementRemainingTaskCount ()
        {
            CheckDisposed ();
            
            SetRemainingTasks (remainingTasks + 1);
            Evaluate ();
            
            return remainingTasks;   
        }        

        public virtual int DecrementRemainingTaskCount ()
        {
            CheckDisposed ();
            
            if (remainingTasks == 0) {
                throw new InvalidOperationException (
                    "Remaining task count cannot be less than 0"
                );
            }
            
            SetRemainingTasks (remainingTasks - 1);
            Evaluate ();
            
            return remainingTasks;
        }        

        public virtual void ResetWait ()
        {
            CheckDisposed ();
            mre.Reset ();
        }

        public virtual void Reset ()
        {
            completedTasks = 0;
            suspendUpdate = false;            
        }

        public virtual bool SetRemainingTasks (int newRemainingTasks)
        {             
            CheckDisposed ();
            if (newRemainingTasks < 0) {
                throw new ArgumentException ("newRemainingTasks must be >= 0");
            }
             
            bool ret = false; 
             
            if (remainingTasks != newRemainingTasks) {
                ret = true;
                remainingTasks = newRemainingTasks;
                OnStatusChanged ();
                Evaluate ();                
            }
                
            return ret;            
        }  

        public virtual void Update ()
        {
            CheckDisposed ();
            OnStatusChanged ();        	    
            Evaluate ();
        }
        
        public virtual void Wait ()
        {           
            CheckDisposed ();                
            mre.WaitOne ();
        }

        protected virtual void CheckDisposed ()
        {
            if (disposed) {
                throw new ObjectDisposedException (GetType ().FullName);
            }     
        }
        
        public virtual void Evaluate ()
        {
            if (suspendUpdate) {
                return;
            }
                             
            if ((remainingTasks == 0 && maxRunningTasks > 0) ||
               (runningTasks < maxRunningTasks &&
               (remainingTasks - runningTasks) > 0)) { 

                mre.Set ();
            }
        }

        protected virtual void SetMaxRunningTasks (int newMaxTasks)
        {
            CheckDisposed ();
                
            if (newMaxTasks < 0) {
                throw new ArgumentException ("newMaxTasks must be >= 0");
            }
                
            if (maxRunningTasks != newMaxTasks) {
                maxRunningTasks = newMaxTasks;
                Evaluate ();
            }
        }   
        
        protected virtual void OnStatusChanged (GroupStatusChangedEventArgs e)
        {
            if (suspendUpdate) {
                return;
            }

            EventHandler<GroupStatusChangedEventArgs> handler = StatusChanged;
            
            if (handler != null) {
                handler (this, e);
            }
        }

        protected virtual void OnStatusChanged ()
        {            
            if (suspendUpdate) {
                return;
            }
            
            OnStatusChanged (
                new GroupStatusChangedEventArgs (
                    remainingTasks, runningTasks, completedTasks
                )
            );            
        }        
    }
 }
