using Cryville.Common.Pdt;
using System;
using RBeatTime = Cryville.Crtr.BeatTime;

namespace Cryville.Crtr {
	public abstract class PropSrc {
		int _type;
		byte[] _buf = null;
		public void Invalidate() { _buf = null; }
		public void Get(out int type, out byte[] value) {
			if (_buf == null) InternalGet(out _type, out _buf);
			type = _type;
			value = _buf;
		}
		protected abstract void InternalGet(out int type, out byte[] value);
		public class Arbitrary : PropSrc {
			public int Type { get; private set; }
			readonly byte[] _value;
			public Arbitrary(int type, byte[] value) {
				Type = type;
				_value = value;
			}
			protected override void InternalGet(out int type, out byte[] value) {
				type = Type;
				value = _value;
			}
		}
		public class Boolean : PropSrc {
			readonly Func<bool> _cb;
			public Boolean(Func<bool> cb) { _cb = cb; }
			protected override void InternalGet(out int type, out byte[] value) {
				type = PdtInternalType.Number;
				value = new byte[] { _cb() ? (byte)1 : (byte)0, 0, 0, 0 };
			}
		}
		public class Float : PropSrc {
			readonly Func<float> _cb;
			public Float(Func<float> cb) { _cb = cb; }
			protected override void InternalGet(out int type, out byte[] value) {
				type = PdtInternalType.Number;
				value = BitConverter.GetBytes(_cb());
			}
		}
		public class String : PropSrc {
			readonly Func<string> _cb;
			public String(Func<string> cb) { _cb = cb; }
			protected override unsafe void InternalGet(out int type, out byte[] value) {
				type = PdtInternalType.String;
				var v = _cb();
				int strlen = v.Length;
				value = new byte[strlen * sizeof(char) + sizeof(int)];
				fixed (byte* _ptr = value) {
					char* ptr = (char*)(_ptr + sizeof(int));
					*(int*)_ptr = strlen;
					int i = 0;
					foreach (var c in v) ptr[i++] = c;
				}
			}
		}
		public class Identifier : PropSrc {
			readonly Func<int> _cb;
			public Identifier(Func<int> cb) { _cb = cb; }
			protected override void InternalGet(out int type, out byte[] value) {
				type = PdtInternalType.Undefined;
				value = BitConverter.GetBytes(_cb());
			}
		}
		public class BeatTime : PropSrc {
			readonly Func<RBeatTime> _cb;
			public BeatTime(Func<RBeatTime> cb) { _cb = cb; }
			protected override unsafe void InternalGet(out int type, out byte[] value) {
				var bt = _cb();
				type = PdtInternalType.Vector;
				value = new byte[4 * sizeof(int)];
				fixed (byte* _ptr = value) {
					int* ptr = (int*)_ptr;
					*ptr++ = bt.b;
					*ptr++ = bt.n;
					*ptr++ = bt.d;
					*ptr++ = PdtInternalType.Number;
				}
			}
		}
	}
}
