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

public class EventUtil
{
    public delegate void SimpleEventFunction<T>(T handler);

    private static Dictionary<Type, List<GameObject>> m_targets = new Dictionary<Type, List<GameObject>>();

    public static void Subscribe<TEvent>(GameObject listener) where TEvent : IEventSystemHandler
    {
        var type = typeof(TEvent);
        if (!m_targets.ContainsKey(type))
            m_targets[type] = new List<GameObject>();

        m_targets[type].Add(listener);
    }

    public static void Unsubscribe<TEvent>(GameObject listener) where TEvent : IEventSystemHandler
    {
        var type = typeof(TEvent);
        if (!m_targets.ContainsKey(type) || !m_targets[type].Remove(listener))
            throw new ArgumentException("The given listener isn't subscribed to the given Event.");
    }

    public static void UnsubscribeAll()
    {
        m_targets = new Dictionary<Type, List<GameObject>>();
    }

    /**
    * Send a message to all objects with the given component type TComponent.
    * 
    * Example: EventUtil.sendToAll<IMyEvent>((x, y) => x.someMessage(someArguments...))
    * where x is the event handler that implements IMyEvent
    * and y is the eventData
    *
    * Returns true if at least one receiver/eventHandler received the event and all events were transmitted successfully
    */
    public static bool SendToAllWithComponent<TEvent, TComponent>(BaseEventData eventData, ExecuteEvents.EventFunction<TEvent> functor) 
        where TEvent : IEventSystemHandler where TComponent : UnityEngine.Object
    {
        bool r = true;

        TComponent[] targets = UnityEngine.Object.FindObjectsOfType<TComponent>();

        foreach(TComponent t in targets)
            r &= ExecuteEvents.Execute(t as GameObject, eventData, functor);

        return targets.Length > 0 && r;
    }

    /**
    * Send a message to all game objects which subsrcibed for TEvent.
    * 
    * Example: EventUtil.sendToAll<IMyEvent>((x, y) => x.someMessage(someArguments...))
    * where x is the event handler that implements IMyEvent
    * and y is the eventData
    *
    * Returns true if at least one receiver/eventHandler received the event and all events were transmitted successfully
    */
    public static bool SendToAll<TEvent>(BaseEventData eventData, ExecuteEvents.EventFunction<TEvent> functor) where TEvent : IEventSystemHandler
    {
        var type = typeof(TEvent);
        if (!m_targets.ContainsKey(type))
            return false;

        bool r = true;

        var targets = m_targets[type];

        foreach (var t in targets)
            r &= ExecuteEvents.Execute(t, eventData, functor);

        return r;
    }

    /**
    * Send a message to all objects which subsrcibed for TEvent.
    * 
    * Example: EventUtil.sendToAll<IMyEvent>(x => x.someMessage(someArguments...))
    * where x is the event handler that implements IMyEvent
    *
    * Returns true if at least one receiver/eventHandler received the event and all events were transmitted successfully
    */
    public static bool SendToAll<TEvent>(SimpleEventFunction<TEvent> functor) where TEvent : IEventSystemHandler
    {
        return SendToAll<TEvent>(null, (x, y) => functor.Invoke(x));
    }
}
