using Cryville.Common.Pdt;
using System;
using RBeatTime = Cryville.Crtr.BeatTime;
using RVector4 = UnityEngine.Vector4;

namespace Cryville.Crtr {
	public abstract class PropSrc {
		public int Type { get; private set; }
		public PropSrc(int type) {
			Type = type;
		}
		protected byte[] buf = null;
		protected virtual bool Invalidated { get { return buf == null; } }
		public virtual void Invalidate() { buf = null; }
		public void Get(out int type, out byte[] value) {
			if (Invalidated) InternalGet();
			type = Type;
			value = buf;
		}
		protected abstract void InternalGet();
		public abstract class Fixed<T> : PropSrc {
			protected readonly Func<T> _cb;
			public Fixed(int type, Func<T> cb) : base(type) { _cb = cb; }
		}
		public abstract class FixedBuffer<T> : Fixed<T> {
			bool m_invalidated = true;
			protected override bool Invalidated { get { return m_invalidated; } }
			public override void Invalidate() { m_invalidated = true; }
			public FixedBuffer(int type, int size, Func<T> cb) : base(type, cb) { buf = new byte[size]; }
			protected override void InternalGet() {
				m_invalidated = false;
			}
		}
		public class Arbitrary : PropSrc {
			readonly byte[] _value;
			public Arbitrary(int type, byte[] value) : base(type) {
				_value = value;
			}
			protected override void InternalGet() {
				buf = _value;
			}
		}
		public class Boolean : FixedBuffer<bool> {
			public Boolean(Func<bool> cb) : base(PdtInternalType.Number, 4, cb) { }
			protected override void InternalGet() {
				base.InternalGet();
				buf[0] = _cb() ? (byte)1 : (byte)0;
			}
		}
		public static readonly PropSrc Error = new Arbitrary(PdtInternalType.Error, new byte[0]);
		public class Float : FixedBuffer<float> {
			public Float(Func<float> cb) : base(PdtInternalType.Number, 4, cb) { }
			protected override unsafe void InternalGet() {
				base.InternalGet();
				fixed (byte* _ptr = buf) {
					*(float*)_ptr = _cb();
				}
			}
		}
		public static readonly PropSrc Null = new Arbitrary(PdtInternalType.Null, new byte[0]);
		public class String : Fixed<string> {
			public String(Func<string> cb) : base(PdtInternalType.String, cb) { }
			protected override unsafe void InternalGet() {
				var v = _cb();
				int strlen = v.Length;
				buf = new byte[strlen * sizeof(char) + sizeof(int)];
				fixed (byte* _ptr = buf) {
					char* ptr = (char*)(_ptr + sizeof(int));
					*(int*)_ptr = strlen;
					int i = 0;
					foreach (var c in v) ptr[i++] = c;
				}
			}
		}
		public class Identifier : FixedBuffer<int> {
			public Identifier(Func<int> cb) : base(PdtInternalType.Undefined, 4, cb) { }
			protected override unsafe void InternalGet() {
				base.InternalGet();
				fixed (byte* _ptr = buf) {
					*(int*)_ptr = _cb();
				}
			}
		}
		public class BeatTime : FixedBuffer<RBeatTime> {
			public BeatTime(Func<RBeatTime> cb) : base(PdtInternalType.Vector, 4 * sizeof(int), cb) { }
			protected override unsafe void InternalGet() {
				base.InternalGet();
				var bt = _cb();
				fixed (byte* _ptr = buf) {
					int* ptr = (int*)_ptr;
					*ptr++ = bt.b;
					*ptr++ = bt.n;
					*ptr++ = bt.d;
					*ptr++ = PdtInternalType.Number;
				}
			}
		}
		public class Vector4 : FixedBuffer<RVector4> {
			public Vector4(Func<RVector4> cb) : base(PdtInternalType.Vector, 4 * sizeof(float) + sizeof(int), cb) { }
			protected override unsafe void InternalGet() {
				base.InternalGet();
				var vec = _cb();
				fixed (byte* _ptr = buf) {
					float* ptr = (float*)_ptr;
					*ptr++ = vec.x;
					*ptr++ = vec.y;
					*ptr++ = vec.z;
					*ptr++ = vec.w;
					*(int*)ptr = PdtInternalType.Number;
				}
			}
		}
	}
}
