namespace Fusion.Addons.AnimationController
{
	using System.Collections.Generic;
	public unsafe interface IAnimationPropertyProvider
	{
		int WordCount { get; }
		void Initialize(AnimationController controller);
		void Deinitialize();
		void Read(AnimationReadWriteInfo readWriteInfo);
		void Write(AnimationReadWriteInfo readWriteInfo);
		void Interpolate(ref AnimationInterpolationInfo interpolationInfo);
	}
	/// 
	/// AnimationPropertyProvider handles synchronization and interpolation of animation properties.
	/// Animation properties are provided by type T which can be implemented by AnimationController, AnimationLayer or AnimationState.
	/// T can be implemented only once by a single controller/layer/state type.
	/// All animation property providers must be registered in AnimationController.AddAnimationPropertyProviders().
	/// 
	public abstract unsafe class AnimationPropertyProvider : IAnimationPropertyProvider where T : class
	{
		// PRIVATE MEMBERS
		private T[] _items;
		private int _wordCount;
		// IAnimationPropertyProvider INTERFACE
		int IAnimationPropertyProvider.WordCount => _wordCount;
		void IAnimationPropertyProvider.Initialize(AnimationController controller)
		{
			List items = new List();
			AddController(controller, items);
			_items = items.ToArray();
			_wordCount = default;
			for (int i = 0, count = _items.Length; i < count; ++i)
			{
				_wordCount += GetWordCount(_items[i]);
			}
			OnInitialize(controller);
		}
		void IAnimationPropertyProvider.Deinitialize()
		{
			OnDeinitialize();
			_items     = default;
			_wordCount = default;
		}
		void IAnimationPropertyProvider.Read(AnimationReadWriteInfo readWriteInfo)
		{
			for (int i = 0, count = _items.Length; i < count; ++i)
			{
				Read(_items[i], readWriteInfo);
			}
		}
		void IAnimationPropertyProvider.Write(AnimationReadWriteInfo readWriteInfo)
		{
			for (int i = 0, count = _items.Length; i < count; ++i)
			{
				Write(_items[i], readWriteInfo);
			}
		}
		void IAnimationPropertyProvider.Interpolate(ref AnimationInterpolationInfo interpolationInfo)
		{
			for (int i = 0, count = _items.Length; i < count; ++i)
			{
				Interpolate(_items[i], ref interpolationInfo);
			}
		}
		// PROTECTED METHODS
		protected abstract int  GetWordCount(T item);
		protected abstract void Read(T item, AnimationReadWriteInfo info);
		protected abstract void Write(T item, AnimationReadWriteInfo info);
		protected abstract void Interpolate(T item, ref AnimationInterpolationInfo interpolationInfo);
		protected virtual void OnInitialize(AnimationController controller) {}
		protected virtual void OnDeinitialize()                             {}
		// PRIVATE METHODS
		private void AddController(AnimationController controller, List items)
		{
			if (controller is T item)
			{
				items.Add(item);
			}
			IList layers = controller.Layers;
			for (int i = 0, count = layers.Count; i < count; ++i)
			{
				AddLayer(layers[i], items);
			}
		}
		private void AddLayer(AnimationLayer layer, List items)
		{
			if (layer is T item)
			{
				items.Add(item);
			}
			IList states = layer.States;
			for (int i = 0, count = states.Count; i < count; ++i)
			{
				AddState(states[i], items);
			}
		}
		private void AddState(AnimationState state, List items)
		{
			if (state is T item)
			{
				items.Add(item);
			}
			IList states = state.States;
			for (int i = 0, count = states.Count; i < count; ++i)
			{
				AddState(states[i], items);
			}
		}
	}
}