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

namespace mgaia.Utils
{
    /// <summary>
    /// Classe que representa uma central de eventos
    /// </summary>
    /// <owner>Victor</owner>
    public abstract class EventCentral
    {
        private Dictionary<string, FieldInfo> events;

        public EventCentral()
        {
            LoadEvents();
        }

        /// <summary>
        /// Faz um pre-load de todos os campos que são eventos
        /// </summary>
        private void LoadEvents()
        {
            events = new Dictionary<string, FieldInfo>();

            Type centralType = GetType();

#if UNITY_METRO && !UNITY_EDITOR
            foreach(FieldInfo fieldInfo in centralType.GetTypeInfo().DeclaredFields)
#else
            foreach (FieldInfo fieldInfo in centralType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
#endif
            {
#if UNITY_METRO && !UNITY_EDITOR
                if (fieldInfo.FieldType.GetTypeInfo().IsSubclassOf(typeof(Delegate)))
#else
                if (fieldInfo.FieldType.IsSubclassOf(typeof(Delegate)))
#endif
                {
                    events.Add(fieldInfo.Name, fieldInfo);
                }
            }

        }

        /// <summary>
        /// Dispara um evento pelo nome
        /// </summary>
        public void RaiseEvent(string eventName, params object[] parameters)
        {
            FieldInfo fieldInfo = events[eventName];
            
            object value = fieldInfo.GetValue(this);

            MulticastDelegate eventDelegate = value as MulticastDelegate;

            if (eventDelegate != null)
            {
                foreach (Delegate @delegate in eventDelegate.GetInvocationList())
                {
#if UNITY_EDITOR
                    ParameterInfo[] parametersInfos = @delegate.Method.GetParameters();
                    for (uint idx = 0; idx < parametersInfos.Length; idx++)
                    {
                        ParameterInfo parameterInfo = parametersInfos[idx];

                        Type parameterType = parameterInfo.ParameterType;
                        Type sentType = parameters[idx].GetType();

                        Assert.AssertTrue(parameterType.IsAssignableFrom(sentType), "O tipo do parâmetro não condiz com o tipo enviado! Tipo do parametro: " + parameterType + " , Valor enviado: " + sentType + ", Evento: " + eventName);
                    }
#endif

#if UNITY_METRO && !UNITY_EDITOR
                    @delegate.GetMethodInfo().Invoke(@delegate.Target, parameters);
#else
                    @delegate.Method.Invoke(@delegate.Target, parameters);
#endif
                }
            }
        }
    }
}