using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;

namespace OpenSEMI.Core.Container
{
	public class w : IContainer, IServiceProvider
	{
		private readonly ConcurrentDictionary<Type, CachedTypeInfo> _serviceTypeLookup = new ConcurrentDictionary<Type, CachedTypeInfo>();

		private readonly ConcurrentDictionary<Type, object> _serviceInstanceLookup = new ConcurrentDictionary<Type, object>();

		private readonly ConcurrentDictionary<Type, Action<object>> _serviceTypeCallbackLookup = new ConcurrentDictionary<Type, Action<object>>();

		public void Register<TService, TImplementation>() where TImplementation : TService
		{
			_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo
			{
				Type = typeof(TImplementation),
				IsSingleton = true
			};
		}

		public void Register<TService, TImplementation>(bool singleton = true) where TImplementation : TService
		{
			_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo
			{
				Type = typeof(TImplementation),
				IsSingleton = singleton
			};
		}

		public void Register<TService>(Type implementationType, bool singleton = true)
		{
			if (implementationType == (Type)null)
			{
				throw new ArgumentNullException("implementationType cannot be null.");
			}
			_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo
			{
				Type = implementationType,
				IsSingleton = singleton
			};
		}

		public void Register<TService>(Type implementationType, Action<TService> callback, bool singleton = true)
		{
			if (implementationType == (Type)null)
			{
				throw new ArgumentNullException("serviceType cannot be null.");
			}
			_serviceTypeLookup[typeof(TService)] = new CachedTypeInfo
			{
				Type = implementationType,
				IsSingleton = singleton
			};
			if (callback != null)
			{
				_serviceTypeCallbackLookup[typeof(TService)] = delegate(object x)
				{
					callback((TService)x);
				};
			}
		}

		public void Register(Type serviceType, Type implementationType, bool singleton = true)
		{
			if (serviceType == (Type)null)
			{
				throw new ArgumentNullException("serviceType cannot be null.");
			}
			if (implementationType == (Type)null)
			{
				throw new ArgumentNullException("serviceType cannot be null.");
			}
			if (!serviceType.IsAssignableFrom(implementationType))
			{
				throw new ArgumentException($"Service could not be registered. {implementationType.Name} does not implement {serviceType.Name}.");
			}
			_serviceTypeLookup[serviceType] = new CachedTypeInfo
			{
				Type = implementationType,
				IsSingleton = singleton
			};
		}

		public void Register<TService>(TService instance)
		{
			if (instance == null)
			{
				throw new ArgumentNullException("instance cannot be null.");
			}
			_serviceInstanceLookup[typeof(TService)] = instance;
		}

		public T Resolve<T>()
		{
			return (T)Resolve(typeof(T));
		}

		private object Resolve(Type type)
		{
			object value = null;
			if (!_serviceTypeLookup.TryGetValue(type, out CachedTypeInfo value2))
			{
				Register(type, type, true);
				CachedTypeInfo cachedTypeInfo = default(CachedTypeInfo);
				cachedTypeInfo.Type = type;
				cachedTypeInfo.IsSingleton = true;
				value2 = cachedTypeInfo;
			}
			if (_serviceInstanceLookup.TryGetValue(type, out value))
			{
				return value;
			}
			ConstructorInfo constructor = ConstructorCache.GetConstructor(value2.Type);
			if (constructor != (ConstructorInfo)null)
			{
				List<ParameterInfo> parameters = ParameterCache.GetParameters(constructor);
				List<object> list = new List<object>();
				foreach (ParameterInfo item in parameters)
				{
					list.Add(Resolve(item.ParameterType));
				}
				object obj = Activator.CreateInstance(value2.Type, list.ToArray());
				if (_serviceTypeCallbackLookup.TryGetValue(type, out Action<object> value3))
				{
					value3(obj);
				}
				if (value2.IsSingleton)
				{
					_serviceInstanceLookup[type] = obj;
				}
				return obj;
			}
			return null;
		}

		public bool IsRegistered<TService>()
		{
			if (_serviceTypeLookup.ContainsKey(typeof(TService)) || _serviceInstanceLookup.ContainsKey(typeof(TService)))
			{
				return true;
			}
			return false;
		}

		public object GetService(Type serviceType)
		{
			return Resolve(serviceType);
		}
	}
}