using Cryville.Common.Collections.Specialized;
using System;

namespace Cryville.Crtr.Event {
	public class RealtimeMotionValue {
		Type _type;
		public Vector AbsoluteValue;
		public Vector RelativeValue;
		public IntKeyedDictionary<MotionNode> RelativeNodes;
		internal byte CloneTypeFlag;

		public RealtimeMotionValue Init(Vector init) {
			_type = init.GetType();
			RelativeNodes = new IntKeyedDictionary<MotionNode>();
			AbsoluteValue = init;
			RelativeValue = (Vector)Activator.CreateInstance(_type);
			return this;
		}

		public RealtimeMotionValue Clone() {
			return new RealtimeMotionValue() {
				_type = _type,
				AbsoluteValue = AbsoluteValue.Clone(),
				RelativeValue = RelativeValue.Clone(),
				RelativeNodes = RelativeNodes,
			};
		}

		public void CopyTo(RealtimeMotionValue dest, bool cloneNodes) {
			AbsoluteValue.CopyTo(dest.AbsoluteValue);
			RelativeValue.CopyTo(dest.RelativeValue);
			if (cloneNodes) {
				dest.ReturnAllRelativeNodes();
				foreach (var node in RelativeNodes) {
					var dnode = MotionNodePool.Shared.Rent(_type);
					node.Value.CopyTo(dnode);
					dest.RelativeNodes.Add(node.Key, dnode);
				}
			}
			else dest.RelativeNodes = RelativeNodes;
		}

		public void ReturnAllRelativeNodes() {
			foreach (var node in RelativeNodes) {
				MotionNodePool.Shared.Return(_type, node.Value);
			}
			RelativeNodes.Clear();
		}

		public MotionNode GetRelativeNode(short id) {
			MotionNode result;
			RelativeNodes.TryGetValue(id, out result);
			return result;
		}

		public void SetRelativeNode(MotionNode node) {
			MotionNode cnode;
			if (!RelativeNodes.TryGetValue(node.Id, out cnode)) {
				cnode = MotionNodePool.Shared.Rent(_type);
				cnode.Id = node.Id;
				RelativeNodes.Add(node.Id, cnode);
			}
			if (node.Time != null) node.Time.CopyTo(cnode.Time);
			if (node.EndTime != null) node.EndTime.CopyTo(cnode.EndTime);
			if (node.Value != null) node.Value.CopyTo(cnode.Value);
		}

		/// <summary>
		/// Computes the motion value.
		/// </summary>
		/// <typeparam name="T">The vector type of the value.</typeparam>
		/// <param name="result">The result.</param>
		public void Compute<T>(ref T result) where T : Vector {
			AbsoluteValue.CopyTo(result);
			result.ApplyFrom(RelativeValue);
		}
	}
}
