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

namespace WS
{
	/// <summary>系统处理</summary>
	public class EventSystem : WSComponent
	{
		private readonly Dictionary<long, WSComponent> allComponents = new Dictionary<long, WSComponent>();

		private readonly UnOrderMultiMap<Type, Type> types = new UnOrderMultiMap<Type, Type>();

		private readonly UnOrderMultiMap<Type, IAwakeSystem> awakeSystems = new UnOrderMultiMap<Type, IAwakeSystem>();

		private readonly UnOrderMultiMap<Type, IStartSystem> startSystems = new UnOrderMultiMap<Type, IStartSystem>();

		private readonly UnOrderMultiMap<Type, IUpdateSystem> updateSystems = new UnOrderMultiMap<Type, IUpdateSystem>();

		private readonly UnOrderMultiMap<Type, ILateUpdateSystem> lateUpdateSystems = new UnOrderMultiMap<Type, ILateUpdateSystem>();

		private readonly UnOrderMultiMap<Type, IFixedUpdateSystem> fixedUpdateSystems = new UnOrderMultiMap<Type, IFixedUpdateSystem>();

		private readonly UnOrderMultiMap<Type, IDestroySystem> destroySystems = new UnOrderMultiMap<Type, IDestroySystem>();


		private readonly Queue<long> starts = new Queue<long>();

		private Queue<long> updates = new Queue<long>();
		private Queue<long> updates2 = new Queue<long>();

		private Queue<long> fixedupdates = new Queue<long>();
		private Queue<long> fixedupdates2 = new Queue<long>();

		private Queue<long> lateUpdates = new Queue<long>();
		private Queue<long> lateUpdates2 = new Queue<long>();

		///<summary>加载System脚本</summary>
		public void LoadSystem(Assembly assembly)
		{
			//筛选 BaseAttribute
			Type[] arr = assembly.GetTypes();
			for (int i = 0; i < arr.Length; i++)
			{
				object[] objs = arr[i].GetCustomAttributes(typeof(BaseAttribute), false);
				if (objs.Length == 0)
				{
					continue;
				}
				BaseAttribute baseAttribute = (BaseAttribute)objs[0];
				types.Add(baseAttribute.AttributeType, arr[i]);
			}

			//筛选 ObjectSystemAttribute
			foreach (Type type in types[typeof(ObjectSystemAttribute)])
			{
				object[] objs = type.GetCustomAttributes(typeof(ObjectSystemAttribute), false);
				if (objs.Length == 0)
				{
					continue;
				}
				object obj = Activator.CreateInstance(type);
				switch (obj)
				{
					case IAwakeSystem objectSystem:
						awakeSystems.Add(objectSystem.Type(), objectSystem);
						break;
					case IStartSystem startSystem:
						startSystems.Add(startSystem.Type(), startSystem);
						break;
					case IUpdateSystem updateSystem:
						updateSystems.Add(updateSystem.Type(), updateSystem);
						break;
					case IFixedUpdateSystem fixedUpdateSystem:
						fixedUpdateSystems.Add(fixedUpdateSystem.Type(), fixedUpdateSystem);
						break;
					case ILateUpdateSystem lateUpdateSystem:
						lateUpdateSystems.Add(lateUpdateSystem.Type(), lateUpdateSystem);
						break;
					case IDestroySystem destroySystem:
						destroySystems.Add(destroySystem.Type(), destroySystem);
						break;
				}
			}
		}

		///<summary>获取组件值</summary>
		public List<Type> GetTypes(Type systemAttributeType)
		{
			if (!types.ContainsKey(systemAttributeType))
			{
				return new List<Type>();
			}
			return types[systemAttributeType];
		}

		///<summary>添加组件到运行池</summary>
		public void AddVComponent(WSComponent component)
		{
			allComponents.Add(component.InstanceId, component);

			Type type = component.GetType();

			if (startSystems.ContainsKey(type))
			{
				starts.Enqueue(component.InstanceId);
			}

			if (updateSystems.ContainsKey(type))
			{
				updates.Enqueue(component.InstanceId);
			}

			if (fixedUpdateSystems.ContainsKey(type))
			{
				fixedupdates.Enqueue(component.InstanceId);
			}

			if (lateUpdateSystems.ContainsKey(type))
			{
				lateUpdates.Enqueue(component.InstanceId);
			}
		}

		///<summary>删除组件</summary>
		public void Remove(long instanceId)
		{
			allComponents.Remove(instanceId);
		}

		///<summary>获取组件</summary>
		public WSComponent Get(long instanceId)
		{
			WSComponent component = null;
			allComponents.TryGetValue(instanceId, out component);
			return component;
		}

		#region -- System --
		public void Awake(WSComponent component)
		{
			List<IAwakeSystem> iAwakeSystems = awakeSystems[component.GetType()];
			if (iAwakeSystems == null)
			{
				return;
			}

			foreach (IAwakeSystem aAwakeSystem in iAwakeSystems)
			{
				if (aAwakeSystem == null)
				{
					continue;
				}

				IAwake iAwake = aAwakeSystem as IAwake;
				if (iAwake == null)
				{
					continue;
				}

				try
				{
					iAwake.Run(component);
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}
		}

		public void Awake<P1>(WSComponent component, P1 p1)
		{
			List<IAwakeSystem> iAwakeSystems = awakeSystems[component.GetType()];
			if (iAwakeSystems == null)
			{
				return;
			}

			foreach (IAwakeSystem aAwakeSystem in iAwakeSystems)
			{
				if (aAwakeSystem == null)
				{
					continue;
				}
				IAwake<P1> iAwake = aAwakeSystem as IAwake<P1>;
				if (iAwake == null)
				{
					continue;
				}

				try
				{
					iAwake.Run(component, p1);
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}
		}

		public void Awake<P1, P2>(WSComponent component, P1 p1, P2 p2)
		{
			List<IAwakeSystem> iAwakeSystems = awakeSystems[component.GetType()];
			if (iAwakeSystems == null)
			{
				return;
			}

			foreach (IAwakeSystem aAwakeSystem in iAwakeSystems)
			{
				if (aAwakeSystem == null)
				{
					continue;
				}

				IAwake<P1, P2> iAwake = aAwakeSystem as IAwake<P1, P2>;
				if (iAwake == null)
				{
					continue;
				}

				try
				{
					iAwake.Run(component, p1, p2);
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}
		}

		public void Awake<P1, P2, P3>(WSComponent component, P1 p1, P2 p2, P3 p3)
		{
			List<IAwakeSystem> iAwakeSystems = awakeSystems[component.GetType()];
			if (iAwakeSystems == null)
			{
				return;
			}

			foreach (IAwakeSystem aAwakeSystem in iAwakeSystems)
			{
				if (aAwakeSystem == null)
				{
					continue;
				}

				IAwake<P1, P2, P3> iAwake = aAwakeSystem as IAwake<P1, P2, P3>;
				if (iAwake == null)
				{
					continue;
				}

				try
				{
					iAwake.Run(component, p1, p2, p3);
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}
		}

		private void Start()
		{
			while (starts.Count > 0)
			{
				long instanceId = starts.Dequeue();
				WSComponent component;
				if (!allComponents.TryGetValue(instanceId, out component))
				{
					continue;
				}

				List<IStartSystem> iStartSystems = startSystems[component.GetType()];
				if (iStartSystems == null)
				{
					continue;
				}

				foreach (IStartSystem iStartSystem in iStartSystems)
				{
					try
					{
						iStartSystem.Run(component);
					}
					catch (Exception e)
					{
						Log.LogError(e);
					}
				}
			}
		}

		public void Update()
		{
			Start();

			while (updates.Count > 0)
			{
				long instanceId = updates.Dequeue();
				WSComponent component;
				if (!allComponents.TryGetValue(instanceId, out component))
				{
					continue;
				}
				if (component.IsDisposed)
				{
					continue;
				}

				List<IUpdateSystem> iUpdateSystems = updateSystems[component.GetType()];
				if (iUpdateSystems == null)
				{
					continue;
				}

				updates2.Enqueue(instanceId);

				foreach (IUpdateSystem iUpdateSystem in iUpdateSystems)
				{
					try
					{
						iUpdateSystem.Run(component);
					}
					catch (Exception e)
					{
						Log.LogError(e);
					}
				}
			}

			Swap(ref updates, ref updates2);
		}

		public void FixedUpdate()
		{
			while (fixedupdates.Count > 0)
			{
				long instanceId = fixedupdates.Dequeue();
				WSComponent component;
				if (!allComponents.TryGetValue(instanceId, out component))
				{
					continue;
				}
				if (component.IsDisposed)
				{
					continue;
				}

				List<IFixedUpdateSystem> iFixedUpdateSystems = fixedUpdateSystems[component.GetType()];
				if (iFixedUpdateSystems == null)
				{
					continue;
				}

				fixedupdates2.Enqueue(instanceId);

				foreach (IFixedUpdateSystem iFixedUpdateSystem in iFixedUpdateSystems)
				{
					try
					{
						iFixedUpdateSystem.Run(component);
					}
					catch (Exception e)
					{
						Log.LogError(e);
					}
				}
			}
			Swap(ref fixedupdates, ref fixedupdates2);
		}

		public void LateUpdate()
		{
			while (lateUpdates.Count > 0)
			{
				long instanceId = lateUpdates.Dequeue();
				WSComponent component;
				if (!allComponents.TryGetValue(instanceId, out component))
				{
					continue;
				}
				if (component.IsDisposed)
				{
					continue;
				}

				List<ILateUpdateSystem> iLateUpdateSystems = lateUpdateSystems[component.GetType()];
				if (iLateUpdateSystems == null)
				{
					continue;
				}

				lateUpdates2.Enqueue(instanceId);

				foreach (ILateUpdateSystem iLateUpdateSystem in iLateUpdateSystems)
				{
					try
					{
						iLateUpdateSystem.Run(component);
					}
					catch (Exception e)
					{
						Log.LogError(e);
					}
				}
			}
			Swap(ref lateUpdates, ref lateUpdates2);
		}

		public void Destroy(WSComponent component)
		{
			List<IDestroySystem> iDestroySystems = destroySystems[component.GetType()];
			if (iDestroySystems == null)
			{
				return;
			}

			foreach (IDestroySystem iDestroySystem in iDestroySystems)
			{
				if (iDestroySystem == null)
				{
					continue;
				}

				try
				{
					iDestroySystem.Run(component);
				}
				catch (Exception e)
				{
					Log.LogError(e);
				}
			}
		}

		public void Swap<T>(ref T t1, ref T t2)
		{
			T t3 = t1;
			t1 = t2;
			t2 = t3;
		}

		#endregion

		public override void Dispose()
		{
			base.Dispose();
			allComponents.Clear();
			types.Clear();
			awakeSystems.Clear();
			startSystems.Clear();
			updateSystems.Clear();
			fixedUpdateSystems.Clear();
			lateUpdateSystems.Clear();
			starts.Clear();
			updates.Clear();
			updates2.Clear();
			fixedupdates.Clear();
			fixedupdates2.Clear();
			lateUpdates.Clear();
			lateUpdates.Clear();
		}
	}
}