﻿using System;
using System.Collections.Generic;
using System.FastReflection;
using System.Linq;
using System.Reflection;
using ZKWebStandard.Utils;

namespace ZKWebStandard.Ioc.Extensions {
	/// <summary>
	/// IContainer => IServiceProvider Adapter<br/>
	/// 转换IContainer到IServiceProvider的接口<br/>
	/// </summary>
	public class ServiceProviderAdapter : IServiceProvider {
		/// <summary>
		/// Container<br/>
		/// 容器<br/>
		/// </summary>
		protected IContainer Container { get; set; }
		/// <summary>
		/// MethodInfo of ListFactory<br/>
		/// ListFactory的MethodInfo<br/>
		/// </summary>
		protected MethodInfo ListFactoryMethod { get; set; }
		/// <summary>
		/// MethodInfo of IEnumerableFactory<br/>
		/// IEnumerableFactory的MethodInfo<br/>
		/// </summary>
		protected MethodInfo IEnumerableFactoryMethod { get; set; }
		/// <summary>
		/// MethodInfo of LazyFactory<br/>
		/// LazyFactory的MethodInfo<br/>
		/// </summary>
		protected MethodInfo LazyFactoryMethod { get; set; }
		/// <summary>
		/// MethodInfo of FuncFactory<br/>
		/// FuncFactory的MethodInfo<br/>
		/// </summary>
		protected MethodInfo FuncFactoryMethod { get; set; }

		/// <summary>
		/// Initliaze<br/>
		/// 初始化<br/>
		/// </summary>
		public ServiceProviderAdapter(IContainer container) {
			Container = container;
			ListFactoryMethod = GetType().FastGetMethod(nameof(ListFactory));
			IEnumerableFactoryMethod = GetType().FastGetMethod(nameof(IEnumerableFactory));
			LazyFactoryMethod = GetType().FastGetMethod(nameof(LazyFactory));
			FuncFactoryMethod = GetType().FastGetMethod(nameof(FuncFactory));
		}

		/// <summary>
		/// Resolve List&lt;T&gt;<br/>
		/// 解决List&lt;T&gt;<br/>
		/// </summary>
		public List<T> ListFactory<T>() {
			return Container.ResolveMany<T>().ToList();
		}

		/// <summary>
		/// Resolve IEnumerable&lt;T&gt;<br/>
		/// 解决List&lt;T&gt;<br/>
		/// </summary>
		public IEnumerable<T> IEnumerableFactory<T>() {
			return Container.ResolveMany<T>();
		}

		/// <summary>
		/// Resolve Lazy&lt;T&gt;<br/>
		/// 解决Lazy&lt;T&gt;<br/>
		/// </summary>
		public Lazy<T> LazyFactory<T>(Func<object> resolve) {
			return new Lazy<T>(() => (T)resolve());
		}

		/// <summary>
		/// Resolve Func&lt;T&gt;<br/>
		/// 解决Func&lt;T&gt;<br/>
		/// </summary>
		public Func<T> FuncFactory<T>(Func<object> resolve) {
			return new Func<T>(() =>(T)resolve());
		}

		/// <summary>
		/// Resolve service<br/>
		/// 解决服务<br/>
		/// </summary>
		public object GetService(Type serviceType) {
			var isFunc = false;
			var isLazy = false;
			var isIList = false;
			var isIEnumerable = false;
			// Detect Func<T>
			Type funcReturnType = null;
			if (serviceType.IsGenericType &&
				serviceType.GetGenericTypeDefinition() == typeof(Func<>)) {
				isFunc = true;
				funcReturnType = serviceType.GetGenericArguments()[0];
				serviceType = funcReturnType;
			}
			// Detect Lazy<T>
			if (serviceType.IsGenericType &&
				serviceType.GetGenericTypeDefinition() == typeof(Lazy<>)) {
				isLazy = true;
				serviceType = serviceType.GetGenericArguments()[0];
			}
			// Detect IList<T> and IEnumerable<T>
			if (!serviceType.IsGenericType) {
				// Ordinary service type
			} else if (serviceType.GetGenericTypeDefinition() == typeof(List<>) ||
				serviceType.GetGenericTypeDefinition() == typeof(IList<>) ||
				serviceType.GetGenericTypeDefinition() == typeof(ICollection<>)) {
				isIList = true;
				serviceType = serviceType.GetGenericArguments()[0];
			} else if (serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) {
				isIEnumerable = true;
				serviceType = serviceType.GetGenericArguments()[0];
			}
			// Resolve implementation
			var resolve = new Func<object>(() => {
				if (isIList) {
					return ReflectionUtils.MakeInvoker(ListFactoryMethod, serviceType)(this, null);
				} else if (isIEnumerable) {
					return ReflectionUtils.MakeInvoker(IEnumerableFactoryMethod, serviceType)(this, null);
				}
				return Container.Resolve(serviceType, IfUnresolved.ReturnDefault);
			});
			if (isLazy) {
				var originalResolve = resolve;
				var invoker = ReflectionUtils.MakeInvoker(LazyFactoryMethod, serviceType);
				resolve = () => invoker(this, new object[] { originalResolve });
			}
			if (isFunc) {
				var originalResolve = resolve;
				var invoker = ReflectionUtils.MakeInvoker(FuncFactoryMethod, serviceType);
				resolve = () => invoker(this, new object[] { originalResolve });
			}
			return resolve();
		}
	}
}
