using System;
using System.Collections.Generic;
using UnityEngine;

namespace AX.MessageSystem
{
    /// <summary>
    /// 消息分发中心。
    /// </summary>
    public static class MessageDispatcher
    {
        internal class Pair
        {
            public string MessageType;
            public Action<IMessage> Handler;

            public Pair() { }
            public Pair(string messageType, Action<IMessage> handler)
            {
                MessageType = messageType;
                Handler = handler;
            }
        }

        internal const float Immediate = 0.0f;
        internal const float NextFrame = -1.0f;

        private static Dictionary<string, List<Pair>> filteredHandlers = new Dictionary<string, List<Pair>>();
        private static Dictionary<string, Action<IMessage>> messageHandlers = new Dictionary<string, Action<IMessage>>();

        private static List<IMessage> messages = new List<IMessage>();

        public static void Clear()
        {
            foreach (var pair in filteredHandlers)
                pair.Value.Clear();

            filteredHandlers.Clear();
            messageHandlers.Clear();

            messages.Clear();
        }

        public static void AddListener(string messageType, Action<IMessage> handler)
        {
            AddListener(messageType, handler, default(string));
        }

        public static void AddListener(string messageType, Action<IMessage> handler, string filter)
        {
            if (string.IsNullOrEmpty(messageType) || handler == null)
                throw new ArgumentNullException("messageType or handler is not be null.");

            if (string.IsNullOrEmpty(filter))
            {
                if (messageHandlers.ContainsKey(messageType))
                    messageHandlers[messageType] += handler;
                else
                    messageHandlers[messageType] = handler;
            }
            else
            {
                List<Pair> handlers = null;

                if (filteredHandlers.TryGetValue(filter, out handlers))
                {
                    var index = handlers.FindIndex((pair) => pair.MessageType == messageType);

                    if (index >= 0)
                        handlers[index].Handler += handler;
                    else
                        handlers.Add(new Pair(messageType, handler));
                }
                else
                {
                    var list = new List<Pair>();

                    list.Add(new Pair(messageType, handler));
                    filteredHandlers.Add(filter, list);
                }
            }
        }

        public static void AddListener(string messageType, Action<IMessage> handler, params string[] filters)
        {
            if (string.IsNullOrEmpty(messageType) || handler == null)
                throw new ArgumentNullException("messageType or handler is not be null.");

            for (var i = 0; i < filters.Length; ++i)
            {
                var filter = filters[i];

                if (string.IsNullOrEmpty(filter))
                {
                    if (messageHandlers.ContainsKey(messageType))
                        messageHandlers[messageType] += handler;
                    else
                        messageHandlers[messageType] = handler;
                }
                else
                {
                    List<Pair> handlers = null;

                    if (filteredHandlers.TryGetValue(filter, out handlers))
                    {
                        var index = handlers.FindIndex((pair) => pair.MessageType == messageType);

                        if (index >= 0)
                            handlers[index].Handler += handler;
                        else
                            handlers.Add(new Pair(messageType, handler));
                    }
                    else
                    {
                        var list = new List<Pair>();

                        list.Add(new Pair(messageType, handler));
                        filteredHandlers.Add(filter, list);
                    }
                }
            }
        }

        public static void RemoveListener(string messageType, Action<IMessage> handler)
        {
            RemoveListener(messageType, handler, default(string));
        }

        public static void RemoveListener(string messageType, Action<IMessage> handler, string filter)
        {
            if (string.IsNullOrEmpty(messageType) || handler == null)
                throw new ArgumentNullException("messageType or handler is not be null.");

            if (string.IsNullOrEmpty(filter))
            {
                if (messageHandlers.ContainsKey(messageType))
                {
                    messageHandlers[messageType] -= handler;

                    if (messageHandlers[messageType] == null)
                        messageHandlers.Remove(messageType);
                }
            }
            else
            {
                List<Pair> handlers = null;

                if (filteredHandlers.TryGetValue(filter, out handlers))
                {
                    var index = handlers.FindIndex((pair) => pair.MessageType == messageType);

                    if (index >= 0)
                    {
                        handlers[index].Handler -= handler;

                        if (handlers[index].Handler == null)
                            handlers.RemoveAt(index);
                    }
                }
            }
        }

        public static void RemoveListener(string messageType, Action<IMessage> handler, params string[] filters)
        {
            if (string.IsNullOrEmpty(messageType) || handler == null)
                throw new ArgumentNullException("messageType or handler is not be null.");

            for (var i = 0; i < filters.Length; ++i)
            {
                var filter = filters[i];

                if (string.IsNullOrEmpty(filter))
                {
                    if (messageHandlers.ContainsKey(messageType))
                    {
                        messageHandlers[messageType] -= handler;

                        if (messageHandlers[messageType] == null)
                            messageHandlers.Remove(messageType);
                    }
                }
                else
                {
                    List<Pair> handlers = null;

                    if (filteredHandlers.TryGetValue(filter, out handlers))
                    {
                        var index = handlers.FindIndex((pair) => pair.MessageType == messageType);

                        if (index >= 0)
                        {
                            handlers[index].Handler -= handler;

                            if (handlers[index].Handler == null)
                                handlers.RemoveAt(index);
                        }
                    }
                }
            }
        }

        public static void Update()
        {
            //处理延迟消息并决定何时发送它
            for (int i = 0; i < messages.Count; i++)
            {
                var message = messages[i];

                message.Delay -= Time.deltaTime;

                if (message.Delay < 0)
                    message.Delay = Immediate;

                //是时候发送消息了
                if (!message.IsSent && message.Delay == Immediate)
                    SendMessage(message);
            }

            //回收发送的消息
            for (int i = messages.Count - 1; i >= 0; i--)
            {
                var message = messages[i];

                if (message.IsSent || message.IsHandled)
                {
                    messages.RemoveAt(i);

                    Message.Release((Message)message);
                }
            }
        }

        public static void SendMessage(IMessage message)
        {
            if (message == null || string.IsNullOrEmpty(message.Type))
                throw new ArgumentNullException("message");

            bool missing = true;

            // 处理延迟消息
            if (message.Delay > 0 || message.Delay < 0)
            {
                messages.Add(message);

                missing = false;
            }
            else if (message.Filters == null)
            {
                Action<IMessage> action = null;

                if (messageHandlers.TryGetValue(message.Type, out action))
                {
                    //触发消息回调
                    message.IsSent = true;
                    action(message);
                    message.IsHandled = true;

                    missing = false;
                }
            }
            else
            {
                for (var i = 0; i < message.Filters.Length; ++i)
                {
                    var filter = message.Filters[i];

                    if (string.IsNullOrEmpty(filter))
                    {
                        Action<IMessage> action = null;

                        if (messageHandlers.TryGetValue(message.Type, out action))
                        {
                            message.IsSent = true;
                            action(message);
                            message.IsHandled = true;

                            missing = false;
                        }
                    }
                    else
                    {
                        List<Pair> handlers = null;

                        if (filteredHandlers.TryGetValue(filter, out handlers))
                        {
                            var index = handlers.FindIndex((pair) => pair.MessageType == message.Type);

                            if (index >= 0)
                            {
                                message.IsSent = true;
                                handlers[index].Handler(message);
                                message.IsHandled = true;

                                missing = false;
                            }
                        }
                    }
                }
            }

            //如果没有对应的接收方
            if (missing)
            {
                if (Application.isEditor)
                    Debug.LogError("MessageDispatcher: 该消息类型 { " + message.Type + " } 没有对应的接收方处理。");

                message.IsSent = true;
                message.IsHandled = true;
            }
        }

        public static void SendMessage(string type)
        {
            SendMessage(null, type, default(object), Immediate, default(string));
        }

        public static void SendMessage(string type, string filter)
        {
            SendMessage(null, type, default(object), Immediate, filter);
        }

        public static void SendMessage(string type, float delay)
        {
            SendMessage(null, type, default(object), delay, default(string));
        }

        public static void SendMessage(string type, float delay, string filter)
        {
            SendMessage(null, type, default(object), delay, filter);
        }

        public static void SendMessage(string type, object data)
        {
            SendMessage(null, type, data, Immediate, default(string));
        }

        public static void SendMessage(string type, object data, string filter)
        {
            SendMessage(null, type, data, Immediate, filter);
        }

        public static void SendMessage(string type, object data, float delay)
        {
            SendMessage(null, type, data, delay, default(string));
        }

        public static void SendMessage(string type, object data, float delay, string filter)
        {
            SendMessage(null, type, data, delay, filter);
        }

        public static void SendMessage(object sender, string type)
        {
            SendMessage(sender, type, default(object), Immediate, default(string));
        }

        public static void SendMessage(object sender, string type, float delay)
        {
            SendMessage(sender, type, default(object), delay, default(string));
        }

        public static void SendMessage(object sender, string type, string filter)
        {
            SendMessage(sender, type, default(object), Immediate, filter);
        }

        public static void SendMessage(object sender, string type, float delay, string filter)
        {
            SendMessage(sender, type, default(object), delay, filter);
        }

        public static void SendMessage(object sender, string type, object data)
        {
            SendMessage(sender, type, data, Immediate, default(string));
        }

        public static void SendMessage(object sender, string type, object data, float delay)
        {
            SendMessage(sender, type, data, delay, default(string));
        }

        public static void SendMessage(object sender, string type, object data, string filter)
        {
            SendMessage(sender, type, data, Immediate, filter);
        }

        public static void SendMessage(object sender, string type, object data, float delay, string filter)
        {
            var message = Message.Acquire();

            message.Sender = sender;
            message.Type = type;
            message.Data = data;
            message.Delay = delay;

            if (!string.IsNullOrEmpty(filter))
                message.Filters = new string[] { filter };

            SendMessage(message);

            if (delay == Immediate)
                Message.Release(message);
        }

        public static void SendMessage(object sender, string type, object data, float delay, params string[] filters)
        {
            var message = Message.Acquire();

            message.Sender = sender;
            message.Type = type;
            message.Data = data;
            message.Delay = delay;

            if (filters != null)
                message.Filters = filters;

            SendMessage(message);

            if (delay == Immediate)
                Message.Release(message);
        }
    }
}