using Cryville.Common;
using System;
using UnityEngine;
using Logger = Cryville.Common.Logging.Logger;

namespace Cryville.Crtr.Skin.Components {
	public class SkinAnimation : SkinComponent {
		public SkinAnimation() {
			SubmitProperty("name", new PropOp.Identifier(v => Name = v));
			SubmitProperty("duration", new PropOp.Float(v => Duration = v));
			SubmitProperty("iteration", new PropOp.Float(v => Iteration = v));
			SubmitProperty("direction", new PropOp.Enum<AnimationDirection>(v => Direction = v, v => (AnimationDirection)v));
			SubmitProperty("delay", new PropOp.Float(v => Delay = v));
			Iteration = 1;
		}

		SkinContext _skinContext;
		Transform _writeTransform;

		AnimationSpan _anim;
		int _name;
		public int Name {
			set {
				if (_name == value) return;
				_name = value;
				if (value == 0) {
					_anim = null;
				}
				else {
					var id = new Identifier(value);
					AnimationSpan anim;
					if (!ChartPlayer.pskin.animations.TryGetValue(id, out anim)) {
						Logger.Log("main", 4, "Skin", "Animation {0} not found", id.Name);
						_anim = null;
						return;
					}
					_anim = anim;
				}
			}
		}
		public float Duration { get; private set; }
		public float Iteration { get; private set; }
		public AnimationDirection Direction { get; private set; }
		public float Delay { get; private set; }

		double _startTime;

		public override void Init() {
			_skinContext = new SkinContext(transform);
		}
		public override void Rewind(double time, Transform target) {
			_startTime = time;
			if (target == null) target = transform;
			_writeTransform = target;
		}
		public override void Tick(SkinContainer c, double time) {
			float _rtime = (float)(time - _startTime - Delay) / Duration;
			if (_rtime < 0) _rtime = 0;
			else if (_rtime > Iteration) {
				if (Direction.HasFlag(AnimationDirection.alternate)) {
					_rtime = Iteration % 2;
					if (_rtime > 1) _rtime = 2 - _rtime;
				}
				else {
					_rtime = Iteration % 1;
					if (_rtime == 0) _rtime = 1;
				}
			}
			else {
				if (Direction.HasFlag(AnimationDirection.alternate)) {
					_rtime %= 2;
					if (_rtime > 1) _rtime = 2 - _rtime;
				}
				else {
					_rtime %= 1;
				}
			}
			if (Direction.HasFlag(AnimationDirection.reverse)) _rtime = 1 - _rtime;
			if (_anim != null) c.MatchAnimation(_anim, _rtime, new RuntimeSkinContext(_skinContext, _writeTransform));
		}
		protected override void OnDestroy() { }
	}
	[Flags]
	public enum AnimationDirection {
		normal = 0,
		reverse = 1,
		alternate = 2,
	}
}
