using System;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Connection;
namespace Renci.SshNet.Channels
{
    internal abstract class ClientChannel : Channel
    {
        /// 
        /// Initializes a new  instance.
        /// 
        /// The session.
        /// The local channel number.
        /// Size of the window.
        /// Size of the packet.
        protected ClientChannel(ISession session, uint localChannelNumber, uint localWindowSize, uint localPacketSize)
            : base(session, localChannelNumber, localWindowSize, localPacketSize)
        {
            session.ChannelOpenConfirmationReceived += OnChannelOpenConfirmation;
            session.ChannelOpenFailureReceived += OnChannelOpenFailure;
        }
        /// 
        /// Occurs when  is received.
        /// 
        public event EventHandler OpenConfirmed;
        /// 
        /// Occurs when  is received.
        /// 
        public event EventHandler OpenFailed;
        /// 
        /// Called when channel is opened by the server.
        /// 
        /// The remote channel number.
        /// Initial size of the window.
        /// Maximum size of the packet.
        protected virtual void OnOpenConfirmation(uint remoteChannelNumber, uint initialWindowSize, uint maximumPacketSize)
        {
            InitializeRemoteInfo(remoteChannelNumber, initialWindowSize, maximumPacketSize);
            // Channel is consider to be open when confirmation message was received
            IsOpen = true;
            var openConfirmed = OpenConfirmed;
            if (openConfirmed != null)
                openConfirmed(this, new ChannelOpenConfirmedEventArgs(remoteChannelNumber, initialWindowSize, maximumPacketSize));
        }
        /// 
        /// Send message to open a channel.
        /// 
        /// Message to send
        /// The client is not connected.
        /// The operation timed out.
        /// The size of the packet exceeds the maximum size defined by the protocol.
        protected void SendMessage(ChannelOpenMessage message)
        {
            Session.SendMessage(message);
        }
        /// 
        /// Called when channel failed to open.
        /// 
        /// The reason code.
        /// The description.
        /// The language.
        protected virtual void OnOpenFailure(uint reasonCode, string description, string language)
        {
            var openFailed = OpenFailed;
            if (openFailed != null)
                openFailed(this, new ChannelOpenFailedEventArgs(LocalChannelNumber, reasonCode, description, language));
        }
        private void OnChannelOpenConfirmation(object sender, MessageEventArgs e)
        {
            if (e.Message.LocalChannelNumber == LocalChannelNumber)
            {
                try
                {
                    OnOpenConfirmation(e.Message.RemoteChannelNumber, e.Message.InitialWindowSize,
                        e.Message.MaximumPacketSize);
                }
                catch (Exception ex)
                {
                    OnChannelException(ex);
                }
            }
        }
        private void OnChannelOpenFailure(object sender, MessageEventArgs e)
        {
            if (e.Message.LocalChannelNumber == LocalChannelNumber)
            {
                try
                {
                    OnOpenFailure(e.Message.ReasonCode, e.Message.Description, e.Message.Language);
                }
                catch (Exception ex)
                {
                    OnChannelException(ex);
                }
            }
        }
        protected override void Dispose(bool disposing)
        {
            UnsubscribeFromSessionEvents(Session);
            base.Dispose(disposing);
        }
        /// 
        /// Unsubscribes the current  from session events.
        /// 
        /// The session.
        /// 
        /// Does nothing when  is null.
        /// 
        private void UnsubscribeFromSessionEvents(ISession session)
        {
            if (session == null)
                return;
            session.ChannelOpenConfirmationReceived -= OnChannelOpenConfirmation;
            session.ChannelOpenFailureReceived -= OnChannelOpenFailure;
        }
    }
}