using System;
using System.Collections.Generic;
using UnityEngine;

namespace Cryville.Common.Unity.Input {
	public class UnityKeyHandler<T> : InputHandler where T : UnityKeyReceiver<T> {
		GameObject receiver;
		T recvcomp;

		public UnityKeyHandler() { }

		protected override void ActivateImpl() {
			receiver = new GameObject("__keyrecv__");
			recvcomp = receiver.AddComponent<T>();
			recvcomp.SetCallback(Feed);
		}

		protected override void DeactivateImpl() {
			if (receiver) GameObject.Destroy(receiver);
		}

		public override void Dispose(bool disposing) {
			if (disposing) {
				DeactivateImpl();
			}
		}

		public override bool IsNullable(int type) {
			return false;
		}

		public override byte GetDimension(int type) {
			return 0;
		}

		public override string GetTypeName(int type) {
			return recvcomp.GetKeyName(type);
		}

		public override double GetCurrentTimestamp() {
			return Time.realtimeSinceStartupAsDouble;
		}
	}

	public abstract class UnityKeyReceiver<T> : MonoBehaviour where T : UnityKeyReceiver<T> {
		protected Action<int, int, InputVector> Callback;
		protected readonly List<int> Keys = new List<int>();
		public void SetCallback(Action<int, int, InputVector> h) {
			Callback = h;
		}
		public abstract string GetKeyName(int type);
		void Update() {
			double time = Time.realtimeSinceStartupAsDouble;
			foreach (var k in Keys) {
				Callback(k, 0, new InputVector(time, Vector3.zero));
			}
		}
	}

	public class UnityKeyboardReceiver : UnityKeyReceiver<UnityKeyboardReceiver> {
		public override string GetKeyName(int type) {
			return Enum.GetName(typeof(KeyCode), type);
		}
		void OnGUI() {
			var e = Event.current;
			if (e.keyCode == KeyCode.None) return;
			double time = Time.realtimeSinceStartupAsDouble;
			var key = (int)e.keyCode;
			switch (e.type) {
				case EventType.KeyDown:
					if (!Keys.Contains(key)) {
						Callback(key, 0, new InputVector(time, Vector3.zero));
						Keys.Add(key);
					}
					break;
				case EventType.KeyUp:
					Keys.Remove(key);
					Callback(key, 0, new InputVector(time));
					break;
			}
		}
	}

	public class UnityMouseButtonReceiver : UnityKeyReceiver<UnityMouseButtonReceiver> {
		public override string GetKeyName(int type) {
			switch (type) {
				case 0: return "Mouse Left";
				case 1: return "Mouse Right";
				case 2: return "Mouse Middle";
				default: return string.Format("Mouse Button {0}", type);
			}
		}
		void OnGUI() {
			var e = Event.current;
			double time = Time.realtimeSinceStartupAsDouble;
			var key = e.button;
			switch (e.type) {
				case EventType.MouseDown:
					if (!Keys.Contains(key)) {
						Callback(key, 0, new InputVector(time, Vector3.zero));
						Keys.Add(key);
					}
					break;
				case EventType.MouseUp:
					Keys.Remove(key);
					Callback(key, 0, new InputVector(time));
					break;
			}
		}
	}
}
