using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using VCommon.Collections;
using VCommon.Logging;

namespace VCommon.Reflection
{
    public static class TypeFinder
    {
        public static ILogger Logger { get; set; } = DefaultLogger.Instance;

        private static readonly LazyHolder<IReadOnlyCollection<Type>> Holder = new LazyHolder<IReadOnlyCollection<Type>>(CreateTypeList);

        public static IReadOnlyCollection<Type> AllTypes => Holder.Instance;

        public static Type[] Find(Func<Type, bool> predicate) => AllTypes.Where(predicate).ToArray();

        private static List<Type> CreateTypeList()
        {
            var allTypes = new List<Type>();

            var assemblies = AppDomain.CurrentDomain.GetAssemblies().Distinct();

            foreach (var assembly in assemblies)
            {
                try
                {
                    Type[] typesInThisAssembly;

                    try
                    {
                        typesInThisAssembly = assembly.GetTypes();
                    }
                    catch (ReflectionTypeLoadException ex)
                    {
                        typesInThisAssembly = ex.Types;
                    }

                    if (typesInThisAssembly.IsNullOrEmpty())
                    {
                        continue;
                    }

                    allTypes.AddRange(typesInThisAssembly.Where(type => type != null));
                }
                catch (Exception ex)
                {
                    Logger.Warn(ex.ToString(), ex);
                }
            }

            return allTypes;
        }
    }
}