using Cryville.Common;
using Cryville.Common.Buffers;
using Cryville.Crtr.Components;
using System;
using System.Collections.Generic;
using System.Globalization;
using UnityEngine;

namespace Cryville.Crtr.Event {
	public abstract class ContainerHandler : IDisposable {
		/// <summary>
		/// Prehandling <see cref="ContainerState"/>, prehandling the events.
		/// </summary>
		public ContainerState ps;

		/// <summary>
		/// Backward <see cref="ContainerState"/>, disposing events at the backward clipping plane, firing anchor state and temporary state.
		/// </summary>
		public ContainerState bs;

		/// <summary>
		/// Current <see cref="ContainerState"/>, handling events occuring at the current time.
		/// </summary>
		public ContainerState cs;

		/// <summary>
		/// Anchor <see cref="ContainerState"/>, computing the start position of the temporary state.
		/// </summary>
		public ContainerState ns;

		/// <summary>
		/// Temporary <see cref="ContainerState"/>, rendering events between the clipping planes.
		/// </summary>
		public ContainerState ts;

		/// <summary>
		/// <see cref="GameObject"/> group, the <see cref="Transform"/> containing all the generated elements in the <see cref="ContainerHandler"/>.
		/// </summary>
		public Transform gogroup;

		public readonly Dictionary<int, Anchor> Anchors = new Dictionary<int, Anchor>();
		protected Transform a_cur;
		protected Transform a_head;
		protected Transform a_tail;
		public Vector3 Position {
			get;
			protected set;
		}
		public Quaternion Rotation {
			get;
			protected set;
		}
		public bool Alive {
			get;
			private set;
		}
		public bool Awoken {
			get;
			private set;
		}
		public bool Disposed {
			get;
			private set;
		}

		public EventContainer Container {
			get { return cs.Container; }
		}

		SkinContainer skinContainer;
		protected Judge judge;
		public void AttachSystems(PdtSkin skin, Judge judge) {
			skinContainer = new SkinContainer(skin);
			this.judge = judge;
		}

		public ContainerHandler() { }
		public abstract string TypeName {
			get;
		}
		protected readonly static int _a_cur = IdentifierManager.SharedInstance.Request("cur");
		protected readonly static int _a_head = IdentifierManager.SharedInstance.Request("head");
		protected readonly static int _a_tail = IdentifierManager.SharedInstance.Request("tail");
		public virtual void PreInit() {
			gogroup = new GameObject(TypeName + ":" + Container.GetHashCode().ToString(CultureInfo.InvariantCulture)).transform;
			if (cs.Parent != null)
				gogroup.SetParent(cs.Parent.Handler.gogroup, false);
			a_cur = RegisterAnchor(_a_cur);
			a_head = RegisterAnchor(_a_head);
			a_tail = RegisterAnchor(_a_tail);
		}
		protected Transform RegisterAnchor(int name) {
			var go = new GameObject("." + IdentifierManager.SharedInstance.Retrieve(name)).transform;
			go.SetParent(gogroup, false);
			Anchors.Add(name, new Anchor() { Transform = go });
			return go;
		}

		/// <summary>
		/// Called upon StartUpdate of ps 17.
		/// </summary>
		public virtual void Init() {
			skinContainer.MatchStatic(ps);
			foreach (var i in gogroup.GetComponentsInChildren<SkinComponent>())
				i.Init();
		}
		public virtual void PostInit() {
			gogroup.gameObject.SetActive(false);
		}
		public virtual void Dispose() {
			if (Disposed) return;
			Disposed = true;
			if (gogroup)
				GameObject.Destroy(gogroup.gameObject);
				// gogroup.gameObject.SetActive(false);
			Alive = false;
		}
		protected virtual void PreAwake(ContainerState s) {
			if (gogroup) gogroup.gameObject.SetActive(true);
			Awoken = true; Alive = true;
			OpenAnchor(_a_head);
		}
		protected virtual void Awake(ContainerState s) {
			CloseAnchor(_a_head);
		}
		protected virtual void GetPosition(ContainerState s) { }
		public virtual void StartUpdate(ContainerState s) {
			if (s.CloneType >= 2 && s.CloneType < 16) {
				PreAwake(s);
				Awake(s);
			}
			else if (s.CloneType == 17) {
				Init();
			}
		}
		static readonly SimpleObjectPool<StampedEvent.Anchor> anchorEvPool
			= new SimpleObjectPool<StampedEvent.Anchor>(1024);
		public virtual void Update(ContainerState s, StampedEvent ev) {
			bool flag = !Awoken && s.CloneType >= 2 && s.CloneType < 16;
			if (flag) PreAwake(s);
			if (s.CloneType <= 2) if (gogroup) skinContainer.MatchDynamic(s);
			if (flag) Awake(s);
			if (s.CloneType == 0) {
				var tev = anchorEvPool.Rent();
				tev.Time = s.Time;
				tev.Container = s.Container;
				tev.Name = _a_cur;
				ts.Bus.PushTempEvent(tev);
			}
		}
		public virtual void ExUpdate(ContainerState s, StampedEvent ev) {
			if (ev is StampedEvent.Anchor) {
				var tev = (StampedEvent.Anchor)ev;
				if (gogroup) {
					OpenAnchor(tev.Name);
					skinContainer.MatchDynamic(s);
					CloseAnchor(tev.Name);
				}
				anchorEvPool.Return(tev);
			}
		}
		public virtual void MotionUpdate(byte ct, Chart.Motion ev) { }
		public virtual void EndUpdate(ContainerState s) {
			if (s.CloneType < 16) {
				Awoken = false;
				if (gogroup && s.CloneType <= 2) skinContainer.MatchDynamic(s);
			}
		}
		public virtual void Anchor() { }
		protected void OpenAnchor(int name) {
			if (Anchors.ContainsKey(name)) Anchors[name].Open();
		}
		protected void CloseAnchor(int name) {
			if (Anchors.ContainsKey(name)) Anchors[name].Close();
		}
	}
}
