/********************************
 *
 * 作者:吴隆健
 * 创建时间: 2019/6/29 15:19:34
 * 
 ********************************/

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MrWu.Debug;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace MrWu.RabbitMQ {
    /// <summary>
    /// RPC客户端
    /// </summary>
    public class RpcClient {

        /// <summary>
        /// 发送者
        /// </summary>
        private IModel channel;

        private IConnection connection;

        private readonly object lockObj = new object();

        /// <summary>
        /// rpc结果
        /// </summary>
        private readonly ConcurrentDictionary<string, string> respQueue = new ConcurrentDictionary<string, string>();

        private RabbitConfig config;

        private ushort prefetchCount;

        /// <summary>
        /// 消费者
        /// </summary>
        private EventingBasicConsumer consumer;

        /// <summary>
        /// 创建客户端
        /// </summary>
        /// <param name="config">rabbit配置</param>
        /// <param name="prefetchCount">同时处理的数量</param>
        internal RpcClient(RabbitConfig config, ushort prefetchCount) {
            this.config = config;
            this.prefetchCount = prefetchCount;
            ConnectionFactory factory = new ConnectionFactory();
            factory.UserName = config.username;
            factory.Password = config.password;
            factory.VirtualHost = config.vhost;
            factory.HostName = config.host;
            factory.Port = config.port;
            factory.AutomaticRecoveryEnabled = true;

            connection = factory.CreateConnection();
            CreateChannel();

            //this.CallBackTimeOutCheckTimer = new Timer(CallBackTimeoutCheck, null, 20, 20);
        }

        /// <summary>
        /// 关闭
        /// </summary>
        internal void Close() {
            connection.Close();
        }

        private void CreateChannel() {
            channel = connection.CreateModel();
            var queue = channel.QueueDeclare();
            reply_queue = queue.QueueName;
            consumer = new EventingBasicConsumer(channel);
            consumer.Received += Recive;

            //Debug.Debug.Log("进来两次????");
            //只会同时接收prefetchCount 个数据
            channel.BasicQos(0, prefetchCount, false);
            channel.BasicConsume(reply_queue, true, consumer);
        }

        /// <summary>
        /// rpc 返回值队列
        /// </summary>
        private string reply_queue;

        /// <summary>
        /// 接收到消息
        /// </summary>
        /// <param name="model"></param>
        /// <param name="msg"></param>
        private void Recive(IBasicConsumer model, BasicDeliverEventArgs msg) {
            //Console.WriteLine($"MrWU.RpcClient.Recive\t{DateTime.Now:hh:mm:ss.fffffff}\t{msg?.BasicProperties?.CorrelationId}");
            //ThreadPool.QueueUserWorkItem(HandleRecvMsgThreadPoolMethod, msg);
            Task.Factory.StartNew(
                () => {
                    // Debug.Debug.Log("Rpc接收到消息:" + );
                    if (msg.BasicProperties == null)
                        return;

                    //Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId);

                    if (!respQueue.TryAdd(msg.BasicProperties.CorrelationId, Encoding.UTF8.GetString(msg.Body))) {
                        Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId);
                    }
                }
            );

            /*
            if (msg?.BasicProperties != null) {
                var callbackResult = Encoding.UTF8.GetString(msg.Body);
                //Debug.Debug.Log("RPC 接收到消息:" + msg.BasicProperties.CorrelationId);
                if(AsyncTaskList.TryGetValue(msg.BasicProperties.CorrelationId, out var tuple)) {
                    tuple.Item3.TrySetResult(callbackResult);
                } else if(!respQueue.TryAdd(msg.BasicProperties.CorrelationId, callbackResult)) {
                    Debug.Debug.Error("RPC发送消息失败!" + msg.BasicProperties.CorrelationId);
                }
            }
            */
        }


        /// <summary>
        /// 获取结果
        /// </summary>
        /// <returns></returns>
        private string GetReplyResult(string corrlationId, int waitTime = 3000) {
            DateTime outtime = DateTime.Now.AddMilliseconds(waitTime);
            //while (true) {
            //    //Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId);
            //    if (respQueue.ContainsKey(corrlationId)) {
            //        string msg = null;
            //        if (respQueue.TryGetValue(corrlationId, out msg)) {
            //            return msg;
            //        } else {
            //            Debug.Debug.Error("RPC消息竟被别人取走??");
            //            return null;
            //        }
            //    } else {
            //        Console.WriteLine($"MrWU.RpcClient.GetReplyResult for: {corrlationId},\t{}");
            //    }
            //    if (DateTime.Now > outtime)
            //        return null;
            //    Thread.Sleep(10);
            //}
            while(true) {
                //Debug.Debug.Log("处理处理接收消息:" + Thread.CurrentThread.ManagedThreadId);
                if(respQueue.TryRemove(corrlationId, out var msg)) {
                    return msg;
                } else {
                }
                if(DateTime.Now > outtime)
                    return null;
                Thread.Sleep(10);
            }
        }

        /// <summary>
        /// call 方法
        /// </summary>
        /// <param name="exchange">交换机</param>
        /// <param name="routingkey">路由键</param>
        /// <param name="message">消息</param>
        /// <param name="waitTime">超时时间 毫秒值</param>
        public string Call(string exchange, string routingkey, string message, int waitTime = 3000) {
           // Debug.Debug.Log("call : " + reply_queue);
            string correlationId = MessagePool.GetId();// Guid.NewGuid().ToString();
            try {
                lock(lockObj) {
                    if(channel == null || channel.IsClosed) {
                        CreateChannel();
                    }
                }
                IBasicProperties properties = channel.CreateBasicProperties();
                properties.ReplyTo = reply_queue;
                properties.CorrelationId = correlationId;

                //Debug.Debug.Log("rpcCall :" + correlationId);
                channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message));
                var ret = GetReplyResult(correlationId, waitTime);
                return ret;

            } catch(Exception e) {
                Debug.Debug.Error("Rpc出现错误!" + e.ToString());
                return null;
            }
        }

        //ConcurrentDictionary<string, Tuple<DateTime, string, TaskCompletionSource<string>>> AsyncTaskList { get; } = new ConcurrentDictionary<string, Tuple<DateTime, string, TaskCompletionSource<string>>>();

        /*
        /// <summary>
        /// 异步Callback
        /// </summary>
        /// <param name="exchange"></param>
        /// <param name="routingkey"></param>
        /// <param name="message"></param>
        /// <param name="waitTime"></param>
        /// <returns></returns>
        public Task<string> AsyncCall(string exchange, string routingkey, string message, int waitTime = 3000) {
            var correlationId = Guid.NewGuid().ToString();
            var taskSrc = new TaskCompletionSource<string>();
            try {
                lock(lockObj) {
                    if(channel == null || channel.IsClosed) {
                        CreateChannel();
                    }
                }
                var properties = channel.CreateBasicProperties();
                properties.ReplyTo = reply_queue;
                properties.CorrelationId = correlationId;
                channel.BasicPublish(exchange, routingkey, properties, Encoding.UTF8.GetBytes(message));
                AsyncTaskList.TryAdd(correlationId, new Tuple<DateTime, string, TaskCompletionSource<string>>(DateTime.Now.AddMilliseconds(waitTime), message, taskSrc));
            } catch(Exception ex) {
                Debug.Debug.Error("Rpc出现错误!" + ex.ToString());
                taskSrc.TrySetException(new Exception("AsyncCall Rpc出现错误", ex));
            }
            return taskSrc.Task;
        }
        */

        /*
        Timer CallBackTimeOutCheckTimer { get; }


        void CallBackTimeoutCheck(object obj) {
            var now = DateTime.Now;
            var outdateItems = AsyncTaskList.Where(p => p.Value.Item1 < now).ToArray();
            if(AsyncTaskList.Count>0 && outdateItems.Length == AsyncTaskList.Count) {
                foreach(var item in outdateItems) {
                    if(AsyncTaskList.TryRemove(item.Key, out var tuple)) {
                        //tuple.Item3.TrySetException(new TimeoutException("超时检查: " + tuple.Item2));
                        tuple.Item3.TrySetResult(string.Empty);
                    }
                }
            }
        }
        */
    }
}
