using System;
using System.Threading;
namespace Renci.SshNet.Common
{
    /// 
    /// Base class to encapsulates the results of an asynchronous operation.
    /// 
    public abstract class AsyncResult : IAsyncResult
    {
        // Fields set at construction which never change while operation is pending
        private readonly AsyncCallback _asyncCallback;
        private readonly Object _asyncState;
        // Field set at construction which do change after operation completes
        private const Int32 _statePending = 0;
        private const Int32 _stateCompletedSynchronously = 1;
        private const Int32 _stateCompletedAsynchronously = 2;
        private Int32 _completedState = _statePending;
        // Field that may or may not get set depending on usage
        private ManualResetEvent _asyncWaitHandle;
        // Fields set when operation completes
        private Exception _exception;
        /// 
        /// Gets or sets a value indicating whether EndInvoke has been called on the current AsyncResult.
        /// 
        /// 
        /// 	true if EndInvoke has been called on the current AsyncResult; otherwise, false.
        /// 
        public bool EndInvokeCalled { get; private set; }
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The async callback.
        /// The state.
        public AsyncResult(AsyncCallback asyncCallback, Object state)
        {
            this._asyncCallback = asyncCallback;
            this._asyncState = state;
        }
        /// 
        /// Marks asynchronous operation as completed.
        /// 
        /// The exception.
        /// if set to true [completed synchronously].
        public void SetAsCompleted(Exception exception, Boolean completedSynchronously)
        {
            // Passing null for exception means no error occurred; this is the common case
            this._exception = exception;
            // The m_CompletedState field MUST be set prior calling the callback
            Int32 prevState = Interlocked.Exchange(ref this._completedState,
               completedSynchronously ? _stateCompletedSynchronously : _stateCompletedAsynchronously);
            if (prevState != _statePending)
                throw new InvalidOperationException("You can set a result only once");
            // If the event exists, set it
            if (this._asyncWaitHandle != null)
                this._asyncWaitHandle.Set();
            // If a callback method was set, call it
            if (this._asyncCallback != null)
                this._asyncCallback(this);
        }
        /// 
        /// Waits until the asynchronous operation completes, and then returns. 
        /// 
        public void EndInvoke()
        {
            // This method assumes that only 1 thread calls EndInvoke for this object
            if (!this.IsCompleted)
            {
                // If the operation isn't done, wait for it
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                this._asyncWaitHandle = null;  // Allow early GC
            }
            this.EndInvokeCalled = true;
            // Operation is done: if an exception occurred, throw it
            if (this._exception != null)
                throw new SshException(this._exception.Message, this._exception);
        }
        #region Implementation of IAsyncResult
        /// 
        /// Gets a user-defined object that qualifies or contains information about an asynchronous operation.
        /// 
        /// A user-defined object that qualifies or contains information about an asynchronous operation.
        public Object AsyncState { get { return this._asyncState; } }
        /// 
        /// Gets a value that indicates whether the asynchronous operation completed synchronously.
        /// 
        /// true if the asynchronous operation completed synchronously; otherwise, false.
        public Boolean CompletedSynchronously
        {
            get { return this._completedState == _stateCompletedSynchronously; }
        }
        /// 
        /// Gets a  that is used to wait for an asynchronous operation to complete.
        /// 
        /// A  that is used to wait for an asynchronous operation to complete.
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (this._asyncWaitHandle == null)
                {
                    Boolean done = this.IsCompleted;
                    ManualResetEvent mre = new ManualResetEvent(done);
                    if (Interlocked.CompareExchange(ref this._asyncWaitHandle, mre, null) != null)
                    {
                        // Another thread created this object's event; dispose the event we just created
                        mre.Close();
                    }
                    else
                    {
                        if (!done && this.IsCompleted)
                        {
                            // If the operation wasn't done when we created 
                            // the event but now it is done, set the event
                            this._asyncWaitHandle.Set();
                        }
                    }
                }
                return this._asyncWaitHandle;
            }
        }
        /// 
        /// Gets a value that indicates whether the asynchronous operation has completed.
        /// 
        /// true if the operation is complete; otherwise, false.
        public Boolean IsCompleted
        {
            get { return this._completedState != _statePending; }
        }
        #endregion
    }
    /// 
    /// Base class to encapsulates the results of an asynchronous operation that returns result.
    /// 
    public abstract class AsyncResult : AsyncResult
    {
        // Field set when operation completes
        private TResult _result = default(TResult);
        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// The async callback.
        /// The state.
        public AsyncResult(AsyncCallback asyncCallback, Object state)
            : base(asyncCallback, state)
        { }
        /// 
        /// Marks asynchronous operation as completed.
        /// 
        /// The result.
        /// if set to true [completed synchronously].
        public void SetAsCompleted(TResult result, Boolean completedSynchronously)
        {
            // Save the asynchronous operation's result
            this._result = result;
            // Tell the base class that the operation completed successfully (no exception)
            base.SetAsCompleted(null, completedSynchronously);
        }
        /// 
        /// Waits until the asynchronous operation completes, and then returns the value generated by the asynchronous operation. 
        /// 
        /// 
        new public TResult EndInvoke()
        {
            base.EndInvoke(); // Wait until operation has completed 
            return _result;  // Return the result (if above didn't throw)
        }
    }
}