using System;
using System.Collections.Generic;
using System.Text;
using Taobao.Top.Link.Channel;

namespace Taobao.Top.Link.Endpoints
{
    /// <summary>logic endpoint local proxy object
    /// </summary>
    public class EndpointProxy
    {
        private IList<IChannelSender> _senders;
        private Random _random;
        private EndpointHandler _handler;

        /// <summary>get id
        /// </summary>
        public Identity Identity { get; internal set; }
        /// <summary>known by both side, like a sessionId
        /// </summary>
        public string Token { get; internal set; }

        public EndpointProxy(EndpointHandler handler)
        {
            this._senders = new List<IChannelSender>();
            this._random = new Random();
            this._handler = handler;
        }

        internal void Add(IChannelSender sender)
        {
            if (!this._senders.Contains(sender))
                lock (this._senders)
                    if (!this._senders.Contains(sender))
                        this._senders.Add(sender);
        }

        internal void Remove(IChannelSender sender)
        {
            lock (this._senders)
                this._senders.Remove(sender);
        }

        internal void Remove(string uri)
        {
            lock (this._senders)
            {
                for (var i = 0; i < this._senders.Count; i++)
                {
                    var channel = this._senders[i] as IClientChannel;
                    if (channel == null)
                        continue;
                    if (!channel.Uri.ToString().Equals(uri))
                        continue;
                    this._senders.Remove(channel);
                    i--;
                }
            }
        }

        internal void Close(string uri, string reason)
        {
            lock (this._senders)
            {
                for (var i = 0; i < this._senders.Count; i++)
                {
                    var channel = this._senders[i] as IClientChannel;
                    if (channel == null)
                        continue;
                    if (!channel.Uri.ToString().Equals(uri))
                        continue;
                    channel.Close(reason);
                    this._senders.Remove(channel);
                    i--;
                }
            }
        }

        /// <summary>check is there any sender can be used to send
        /// </summary>
        /// <returns></returns>
        public bool hasValidSender()
        {
            foreach (IClientChannel sender in this._senders)
            {
                if (sender.IsConnected)
                    return true;
            }
            return false;
        }
        /// <summary>send message and wait reply
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public IDictionary<string, object> SendAndWait(IDictionary<string, object> message)
        {
            return this.SendAndWait(message, Endpoint.TIMEOUT);
        }
        /// <summary>send message and wait reply
        /// </summary>
        /// <param name="message"></param>
        /// <param name="timeout">timeout in milliseconds</param>
        /// <returns></returns>
        public IDictionary<string, object> SendAndWait(IDictionary<string, object> message, int timeout)
        {
            return this.SendAndWait(null, message, timeout);
        }
        /// <summary>send message and wait reply
        /// </summary>
        /// <param name="sender">use to send, must belong this proxy</param>
        /// <param name="message"></param>
        /// <param name="timeout">timeout in milliseconds</param>
        /// <returns></returns>
        internal IDictionary<string, object> SendAndWait(IChannelSender sender, IDictionary<string, object> message, int timeout)
        {
            return this._handler.SendAndWait(this,
                    this.GetSender(sender),
                    this.CreateMessage(message),
                    timeout);
        }
        /// <summary>send message
        /// </summary>
        /// <param name="message"></param>
        public void Send(IDictionary<string, object> message)
        {
            this.Send(null, message);
        }
        /// <summary>send message
        /// </summary>
        /// <param name="sender">use to send, must belong this proxy</param>
        /// <param name="message"></param>
        internal void Send(IChannelSender sender, IDictionary<string, object> message)
        {
            this._handler.Send(this.CreateMessage(message), this.GetSender(sender));
        }

        private Message CreateMessage(IDictionary<string, object> message)
        {
            Message msg = new Message();
            msg.MessageType = MessageType.SEND;
            msg.Content = message;
            msg.Token = this.Token;
            return msg;
        }
        private IChannelSender GetSender(IChannelSender sender)
        {
            if (this._senders.Count == 0)
                throw new ChannelException(Text.E_NO_SENDER);
            if (this._senders.Contains(sender))
                return sender;
            return this._senders[this._random.Next(this._senders.Count)];
        }
    }
}