using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Animations;

namespace MotionFramework.Experimental.Animation
{
	public abstract class AnimNode
	{
		private readonly PlayableGraph _graph;
		private Playable _source;
		private Playable _parent;

		private float _fadeSpeed = 0f;
		private float _fadeWeight = 0f;
		private bool _isFading = false;


		/// <summary>
		/// 是否已经连接
		/// </summary>
		public bool IsConnect { get; private set; } = false;

		/// <summary>
		/// 输入端口
		/// </summary>
		public int InputPort { private set; get; }

		/// <summary>
		/// 是否已经完成
		/// If the duration of the playable is set, when the time of the playable reaches its duration during playback this flag will be set to true.
		/// </summary>
		public bool IsDone
		{
			get
			{
				return _source.IsDone();
			}
		}

		/// <summary>
		/// 是否有效
		/// if the Playable is properly constructed by the PlayableGraph and has not been destroyed, false otherwise.
		/// </summary>
		public bool IsValid
		{
			get
			{
				return _source.IsValid();
			}
		}

		/// <summary>
		/// 是否正在播放中
		/// </summary>
		public bool IsPlaying
		{
			get
			{
				return _source.GetPlayState() == PlayState.Playing;
			}
		}

		/// <summary>
		/// 时间轴
		/// </summary>
		public float Time
		{
			set
			{
				_source.SetTime(value);
			}
			get
			{
				return (float)_source.GetTime();
			}
		}

		/// <summary>
		/// 播放速度
		/// </summary>
		public float Speed
		{
			set
			{
				_source.SetSpeed(value);
			}
			get
			{
				return (float)_source.GetSpeed();
			}
		}

		/// <summary>
		/// 权重值
		/// </summary>
		public float Weight
		{
			set
			{
				_parent.SetInputWeight(InputPort, value);
			}
			get
			{
				return _parent.GetInputWeight(InputPort);
			}
		}


		public AnimNode(PlayableGraph graph)
		{
			_graph = graph;
		}
		public virtual void Update(float deltaTime)
		{
			if (_isFading)
			{
				Weight = Mathf.MoveTowards(Weight, _fadeWeight, _fadeSpeed * deltaTime);
				if (Mathf.Approximately(Weight, _fadeWeight))
				{
					_isFading = false;
				}
			}
		}
		public virtual void Destroy()
		{
			if (IsValid)
			{
				_graph.DestroySubgraph(_source);
			}
		}
		public virtual void PlayNode()
		{
			// NOTE : When playing, the local time of this Playable will be updated during the evaluation of the PlayableGraph.
			_source.Play();

			// NOTE : Changes a flag indicating that a playable has completed its operation.
			// Playable that reach the end of their duration are automatically marked as done.
			_source.SetDone(false);
		}
		public virtual void PauseNode()
		{
			// NOTE : When paused, the local time of this Playable will not be updated during the evaluation of the PlayableGraph.
			_source.Pause();

			// NOTE : Changes a flag indicating that a playable has completed its operation.
			// Playable that reach the end of their duration are automatically marked as done.
			_source.SetDone(true);
		}
		public virtual void ResetNode()
		{
			_fadeSpeed = 0;
			_fadeWeight = 0;
			_isFading = false;

			Time = 0;
			Speed = 1;
			Weight = 0;
		}

		/// <summary>
		/// 连接到父节点
		/// </summary>
		/// <param name="parent">父节点对象</param>
		/// <param name="inputPort">父节点上的输入端口</param>
		public void Connect(Playable parent, int parentInputPort)
		{
			if (IsConnect)
				throw new System.Exception("AnimNode is connected.");

			_parent = parent;
			InputPort = parentInputPort;

			// 重置节点
			ResetNode();

			// 连接
			_graph.Connect(_source, 0, parent, parentInputPort);
			IsConnect = true;
		}

		/// <summary>
		/// 同父节点断开连接
		/// </summary>
		public void Disconnect()
		{
			if (IsConnect == false)
				throw new System.Exception("AnimNode is disconnected.");

			// 断开
			_graph.Disconnect(_parent, InputPort);
			IsConnect = false;
		}

		/// <summary>
		/// 开始权重值过渡
		/// </summary>
		/// <param name="destWeight">目标权重值</param>
		/// <param name="fadeDuration">过渡时间</param>
		public void StartWeightFade(float destWeight, float fadeDuration)
		{
			if (fadeDuration <= 0)
			{
				Weight = destWeight;
				_isFading = false;
				return;
			}

			//注意:保持统一的渐变速度
			_fadeSpeed = 1f / fadeDuration;
			_fadeWeight = destWeight;
			_isFading = true;
		}

		protected void SetSourcePlayable(Playable playable)
		{
			_source = playable;
		}
	}
}