using System;
using Renci.SshNet.Common;
namespace Renci.SshNet
{
    /// 
    /// Base class for port forwarding functionality.
    /// 
    public abstract class ForwardedPort : IForwardedPort
    {
        /// 
        /// Gets or sets the session.
        /// 
        /// 
        /// The session.
        /// 
        internal ISession Session { get; set; }
        /// 
        /// The  event occurs as the forwarded port is being stopped.
        /// 
        internal event EventHandler Closing;
        /// 
        /// The  event occurs as the forwarded port is being stopped.
        /// 
        event EventHandler IForwardedPort.Closing
        {
            add { Closing += value; }
            remove { Closing -= value; }
        }
        /// 
        /// Gets a value indicating whether port forwarding is started.
        /// 
        /// 
        /// true if port forwarding is started; otherwise, false.
        /// 
        public abstract bool IsStarted { get; }
        /// 
        /// Occurs when an exception is thrown.
        /// 
        public event EventHandler Exception;
        /// 
        /// Occurs when a port forwarding request is received.
        /// 
        public event EventHandler RequestReceived;
        /// 
        /// Starts port forwarding.
        /// 
        public virtual void Start()
        {
            CheckDisposed();
            if (IsStarted)
                throw new InvalidOperationException("Forwarded port is already started.");
            if (Session == null)
                throw new InvalidOperationException("Forwarded port is not added to a client.");
            if (!Session.IsConnected)
                throw new SshConnectionException("Client not connected.");
            Session.ErrorOccured += Session_ErrorOccured;
            StartPort();
        }
        /// 
        /// Stops port forwarding.
        /// 
        public virtual void Stop()
        {
            if (IsStarted)
            {
                StopPort(Session.ConnectionInfo.Timeout);
            }
        }
        /// 
        /// Starts port forwarding.
        /// 
        protected abstract void StartPort();
        /// 
        /// Stops port forwarding, and waits for the specified timeout until all pending
        /// requests are processed.
        /// 
        /// The maximum amount of time to wait for pending requests to finish processing.
        protected virtual void StopPort(TimeSpan timeout)
        {
            RaiseClosing();
            var session = Session;
            if (session != null)
            {
                session.ErrorOccured -= Session_ErrorOccured;
            }
        }
        /// 
        /// Releases unmanaged and - optionally - managed resources
        /// 
        /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                var session = Session;
                if (session != null)
                {
                    StopPort(session.ConnectionInfo.Timeout);
                    Session = null;
                }
            }
        }
        /// 
        /// Ensures the current instance is not disposed.
        /// 
        /// The current instance is disposed.
        protected abstract void CheckDisposed();
        /// 
        /// Raises  event.
        /// 
        /// The exception.
        protected void RaiseExceptionEvent(Exception exception)
        {
            var handlers = Exception;
            if (handlers != null)
            {
                handlers(this, new ExceptionEventArgs(exception));
            }
        }
        /// 
        /// Raises  event.
        /// 
        /// Request originator host.
        /// Request originator port.
        protected void RaiseRequestReceived(string host, uint port)
        {
            var handlers = RequestReceived;
            if (handlers != null)
            {
                handlers(this, new PortForwardEventArgs(host, port));
            }
        }
        /// 
        /// Raises the  event.
        /// 
        private void RaiseClosing()
        {
            var handlers = Closing;
            if (handlers != null)
            {
                handlers(this, EventArgs.Empty);
            }
        }
        /// 
        /// Handles session ErrorOccured event.
        /// 
        /// The source of the event.
        /// The  instance containing the event data.
        private void Session_ErrorOccured(object sender, ExceptionEventArgs e)
        {
            RaiseExceptionEvent(e.Exception);
        }
    }
}