using Cryville.Crtr.Components;
using Cryville.Crtr.Event;
using UnityEngine;

namespace Cryville.Crtr {
	class TrackHandler : ContainerHandler {
		readonly GroupHandler gh;
		readonly Chart.Track track;

		public TrackHandler(GroupHandler gh, Chart.Track _track) : base() {
			track = _track;
			this.gh = gh;
		}
		
		public override string TypeName {
			get {
				return "track";
			}
		}

		public override void Init() {
			base.Init();
			sgos = gogroup.GetComponentsInChildren<SectionalGameObject>();
		}
		SectionalGameObject[] sgos;
		Vector3 bpos; Quaternion brot;
		Vector3 spos;
		protected override void PreAwake(ContainerState s) {
			base.PreAwake(s);
			pwp = Vector3.zero;
			cpt = s.ScreenPoint;
			ptime = s.Time;
			if (s.CloneType == 2) {
#if UNITY_5_6_OR_NEWER
				a_head.SetPositionAndRotation(GetCurrentWorldPoint(), Quaternion.Euler(s.Direction));
#else
				a_head.position = GetCurrentWorldPoint();
				a_head.rotation = Quaternion.Euler(s.Direction);
#endif
			}
			else if (s.CloneType == 3) {
				spos = Vector3.zero;
				bpos = cpt;
				brot = Quaternion.Euler(s.Direction);
			}
		}
		protected override void Awake(ContainerState s) {
			base.Awake(s);
			if (!gogroup) return;
			var p = GetCurrentWorldPoint();
			foreach (var i in sgos) {
				i.Reset();
				i.AppendPoint(p, s.QuatDir);
			}
		}

		Vector3 cpt; // Current point
		Vector3 ppt = Vector3.zero; // Previous point
		Vector3 pwp = Vector3.zero; // Previous world point
		float ptime; // Previous time
		float length;

		public override void Update(ContainerState s, StampedEvent ev) {
			base.Update(s, ev);
			if (s.CloneType == 2 || s.CloneType == 3) {
				var tpt = s.ScreenPoint;
				var tsv = s.ScrollVelocity;

				Vector3 dpt = (Vector3)tpt - ppt; // Delta 2D point
				dpt.z = (s.Time - ptime) * ChartPlayer.sv * tsv; // Delta Z
				Quaternion rotq = Quaternion.Euler(s.Direction); // Rotation
				var dwp = rotq * dpt; // Delta world point
				var nl = length + dwp.magnitude; // New length
				var tdist = s.Distance;
				if (nl >= tdist) {
					s.Break();
					return;
				}
				length = nl;
				var wp = pwp + dwp; // World point
				pwp = wp;
				ppt += dpt;

				ptime = s.Time;

				if (!gogroup || s.CloneType == 3) return;
				var p = GetCurrentWorldPoint();
				foreach (var i in sgos)
					i.AppendPoint(p, s.QuatDir);
			}
			else UpdatePosition(s);
		}

		void UpdatePosition(ContainerState s) {
			cpt = s.ScreenPoint;
			ppt = cpt;
			pwp = Vector3.zero;
			ptime = s.Time;
			length = 0;
			//Logger.LogFormat("main", 0, "Debug", "SV: {0}", cs.GetRawValue<Vec1m>("svm").Value);
		}

		// TODO Fix anchor rotation
		public override void Anchor() {
			base.Anchor();
			spos = bpos - GetCurrentWorldPoint();
		}

		public override void EndUpdate(ContainerState s) {
			OpenAnchor("tail");
			base.EndUpdate(s);
			CloseAnchor("tail");
			if (s.CloneType == 0 || s.CloneType == 2) {
				EndUpdatePosition(ts);
				var p = GetCurrentWorldPoint();
				foreach (var i in sgos) {
					i.AppendPoint(p, s.QuatDir);
					i.Seal();
				}
#if UNITY_5_6_OR_NEWER
				a_tail.SetPositionAndRotation(GetCurrentWorldPoint(), Quaternion.Euler(ts.Direction));
#else
				a_tail.position = GetCurrentWorldPoint();
				a_tail.rotation = Quaternion.Euler(ts.Direction);
#endif
			}
			else if (s.CloneType == 3) EndUpdatePosition(ns);
			else if (s.CloneType >= 16) EndUpdatePosition(ps);
		}

		void EndUpdatePosition(ContainerState s) {
			var tpt = s.ScreenPoint;
			var tsv = s.ScrollVelocity;
			
			Vector3 dpt = (Vector3)tpt - ppt;
			dpt.z = (s.Time - ptime) * ChartPlayer.sv * tsv;
			Quaternion rotq = Quaternion.Euler(s.Direction);
			var dwp = rotq * dpt; // Delta world point
			var nl = length + dwp.magnitude; // New length
			var tdist = s.Distance;
			if (nl >= tdist)
				dwp *= (tdist - length) / (nl - length);
			length = nl;
			var wp = pwp + dwp; // World point
			pwp = wp;
			ppt += dpt;
		}

		public Vector3 GetCurrentWorldPoint() {
			return pwp + cpt + spos;
		}
	}
}
