using Framework.DataStructures;
using Framework.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Framework.Sharing
{
    /// <summary>
    /// Implementation the <see cref="Mediatorlike"/> interface. Defines an
    /// object that losely couples subscribers by broadcasting messages 
    /// between them.
    /// </summary>
    /// <typeparam name="MessageType">The base type of the messages to broadcast</typeparam>
    /// <typeparam name="SubscriberType">The base type of the subscribers</typeparam>
    public abstract class Mediator<MessageType, SubscriberType> : Mediatorlike<MessageType, SubscriberType>
    {
        protected ListMap<Type, SubscriberType> messageMap; // Maps message type to a list of subscribers

        /// <summary>
        /// Initializes a new instance of the <see cref="Mediator{MessageType, SubscriberType}"/> class.
        /// </summary>
        public Mediator()
        {
            this.messageMap = new ListMap<Type, SubscriberType>();
        }

        /// <summary>
        /// Broadcasts a message to all subscribers. This implementation
        /// must be defined in a no abstract base class. The subscriber type
        /// must implement a method that receives the message type.
        /// </summary>
        /// <param name="message">The message to transmit.</param>
        /// <returns>Whether or not any subscriber received it.</returns>
        public abstract bool Broadcast(MessageType message);

        /// <summary>
        /// Subscribes an object to this mediator like instance. The 
        /// subscribed instance should now receive messages via the 
        /// broadcast method.
        /// </summary>
        /// <typeparam name="ConcreteMessageType">The message type to transmit</typeparam>
        /// <param name="subscriber">The instance to subscribe</param>
        public void Subscribe<ConcreteMessageType>(SubscriberType subscriber) where ConcreteMessageType : MessageType
        {
            if (!HasSubscriber<ConcreteMessageType>(subscriber))
            {
                this.messageMap.Add(typeof(ConcreteMessageType), subscriber);
                Log("Subscribed " + subscriber + " of message " + typeof(ConcreteMessageType).Name + " to " + this.GetType().Name);
            }
        }

        /// <summary>
        /// Unsubscribes an object from this mediator like instance.
        /// The object will no longer recieve messages via the broadcast
        /// method.
        /// </summary>
        /// <param name="subscriber">The instance to unsubscribe</param>
        public void Unsubscribe<ConcreteMessageType>(SubscriberType subscriber) where ConcreteMessageType : MessageType
        {
            this.messageMap.Remove(typeof(ConcreteMessageType), subscriber);
            Log("Unsubscribed " + subscriber + " of message " + typeof(ConcreteMessageType).Name + " to " + this.GetType().Name);
            //Log(typeof(ConcreteMessageType).Name + " subscribers: " + string.Join(", ", this.messageMap[typeof(ConcreteMessageType)].Select(i => (i as CoreBehaviour).nameStore).ToArray()));
        }

        /// <summary>
        /// Determines whether or not this mediator like instance has a
        /// given subscriber.
        /// </summary>
        /// <param name="subscriber">The subscriber to check for</param>
        /// <returns></returns>
        public bool HasSubscriber<ConcreteMessageType>(SubscriberType subscriber) where ConcreteMessageType : MessageType
        {
            return this.messageMap.Contains(typeof(ConcreteMessageType),subscriber);
        }

        private void Log(string message)
        {
            //Debug.Log(message);
            //Console.WriteLine(message);
        }
    }
}
