//----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------------------

namespace System.ServiceModel.Activation
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;

    class HostedNamedPipeTransportManager : NamedPipeTransportManager
    {
        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile bool settingsApplied;
        Action<Uri> onViaCallback;
        SharedConnectionListener listener;
        ConnectionDemuxer connectionDemuxer;
        int queueId;
        Guid token;
        Func<Uri, int> onDuplicatedViaCallback;

        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile bool demuxerCreated;

        public HostedNamedPipeTransportManager(BaseUriWithWildcard baseAddress)
            : base(baseAddress.BaseAddress)
        {
            this.HostNameComparisonMode = baseAddress.HostNameComparisonMode;
            this.onViaCallback = new Action<Uri>(OnVia);
            this.onDuplicatedViaCallback = new Func<Uri, int>(OnDuplicatedVia);
        }

        protected override bool IsCompatible(NamedPipeChannelListener channelListener)
        {
            if (channelListener.HostedVirtualPath == null)
            {
                return false;
            }

            return base.IsCompatible(channelListener);
        }

        internal void Start(int queueId, Guid token, Action messageReceivedCallback)
        {
            SetMessageReceivedCallback(messageReceivedCallback);
            OnOpenInternal(queueId, token);
        }

        internal override void OnOpen()
        {
            // This is intentionally empty.
        }

        internal override void OnAbort()
        {
        }

        internal void Stop(TimeSpan timeout)
        {
            Cleanup(false, timeout);
        }

        void Cleanup(bool aborting, TimeSpan timeout)
        {
            lock (ThisLock)
            {
                if (listener != null)
                {
                    if (!aborting)
                    {
                        listener.Stop(timeout);
                    }
                    else
                    {
                        listener.Abort();
                    }

                    // The listener will be closed by the demuxer.
                    listener = null;
                }

                if (connectionDemuxer != null)
                {
                    connectionDemuxer.Dispose();
                }

                demuxerCreated = false;
                settingsApplied = false;
            }
        }

        void CreateConnectionDemuxer()
        {
            IConnectionListener connectionListener = new BufferedConnectionListener(listener, MaxOutputDelay, ConnectionBufferSize);
            if (DiagnosticUtility.ShouldUseActivity)
            {
                connectionListener = new TracingConnectionListener(connectionListener, this.ListenUri);
            }

            connectionDemuxer = new ConnectionDemuxer(connectionListener,
                MaxPendingAccepts, MaxPendingConnections, ChannelInitializationTimeout,
                IdleTimeout, MaxPooledConnections,
                OnGetTransportFactorySettings,
                OnGetSingletonMessageHandler,
                OnHandleServerSessionPreamble,
                OnDemuxerError);

            connectionDemuxer.StartDemuxing(onViaCallback);
        }

        void OnOpenInternal(int queueId, Guid token)
        {
            lock (ThisLock)
            {
                this.queueId = queueId;
                this.token = token;

                BaseUriWithWildcard path = new BaseUriWithWildcard(this.ListenUri, this.HostNameComparisonMode);

                listener = new SharedConnectionListener(path, queueId, token, this.onDuplicatedViaCallback);
            }
        }

        internal override void OnClose(TimeSpan timeout)
        {
        }

        void OnVia(Uri address)
        {
            Debug.Print("HostedNamedPipeTransportManager.OnVia() address: " + address + " calling EnsureServiceAvailable()");
            ServiceHostingEnvironment.EnsureServiceAvailable(address.LocalPath);
        }

        protected override void OnSelecting(NamedPipeChannelListener channelListener)
        {
            if (settingsApplied)
            {
                return;
            }

            lock (ThisLock)
            {
                if (settingsApplied)
                {
                    // Use the setting for the first one.
                    return;
                }

                this.ApplyListenerSettings(channelListener);
                settingsApplied = true;
            }
        }

        // This method is called only for the first via of the current proxy.
        int OnDuplicatedVia(Uri via)
        {
            OnVia(via);
            
            if (!demuxerCreated)
            {
                lock (ThisLock)
                {
                    if (listener == null)
                    {
                        // The listener has been stopped.
                        throw FxTrace.Exception.AsError(new CommunicationObjectAbortedException(SR.PipeListenerProxyStopped));
                    }

                    if (!demuxerCreated)
                    {
                        CreateConnectionDemuxer();
                        demuxerCreated = true;
                    }
                }
            }

            return this.ConnectionBufferSize;
        }
    }
}