using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.IO;
using Newtonsoft.Json.Linq;

namespace BizHawk.Emulation.Cores.Computers.AppleII
{
	// barebones classes for writing and reading a simple bson-like format, used to gain a bit of speed in Apple II savestates

	internal enum LBTOK : byte
	{
		Null,
		Undefined,
		StartArray,
		EndArray,
		StartObject,
		EndObject,
		Property,
		S8,
		U8,
		S16,
		U16,
		S32,
		U32,
		S64,
		U64,
		False,
		True,
		String,
		F32,
		F64,
		ByteArray,
	}

	public class LBR : JsonReader
	{
		public LBR(BinaryReader r)
		{
			this.r = r;
		}
		private BinaryReader r;
		public override void Close()
		{
		}
		// as best as I can tell, the serializers refer to depth, but don't actually need to work except when doing certain error recovery
		public override int Depth { get { return 0; } }
		public override string Path { get { throw new NotImplementedException(); } }
		public override Type ValueType { get { return v != null ? v.GetType() : null; } }
		public override JsonToken TokenType { get { return t; } }
		public override object Value { get { return v; } }
		private object v;
		private JsonToken t;

		public override bool Read()
		{
			LBTOK l = (LBTOK)r.ReadByte();
			switch (l)
			{
				case LBTOK.StartArray: t = JsonToken.StartArray; v = null; break;
				case LBTOK.EndArray: t = JsonToken.EndArray; v = null; break;
				case LBTOK.StartObject: t = JsonToken.StartObject; v = null; break;
				case LBTOK.EndObject: t = JsonToken.EndObject; v = null; break;
				case LBTOK.Null: t = JsonToken.Null; v = null; break;
				case LBTOK.False: t = JsonToken.Boolean; v = false; break;
				case LBTOK.True: t = JsonToken.Boolean; v = true; break;
				case LBTOK.Property: t = JsonToken.PropertyName; v = r.ReadString(); break;
				case LBTOK.Undefined: t = JsonToken.Undefined; v = null; break;
				case LBTOK.S8: t = JsonToken.Integer; v = r.ReadSByte(); break;
				case LBTOK.U8: t = JsonToken.Integer; v = r.ReadByte(); break;
				case LBTOK.S16: t = JsonToken.Integer; v = r.ReadInt16(); break;
				case LBTOK.U16: t = JsonToken.Integer; v = r.ReadUInt16(); break;
				case LBTOK.S32: t = JsonToken.Integer; v = r.ReadInt32(); break;
				case LBTOK.U32: t = JsonToken.Integer; v = r.ReadUInt32(); break;
				case LBTOK.S64: t = JsonToken.Integer; v = r.ReadInt64(); break;
				case LBTOK.U64: t = JsonToken.Integer; v = r.ReadUInt64(); break;
				case LBTOK.String: t = JsonToken.String; v = r.ReadString(); break;
				case LBTOK.F32: t = JsonToken.Float; v = r.ReadSingle(); break;
				case LBTOK.F64: t = JsonToken.Float; v = r.ReadDouble(); break;
				case LBTOK.ByteArray: t = JsonToken.Bytes; v = r.ReadBytes(r.ReadInt32()); break;

				default:
					throw new InvalidOperationException();
			}
			return true;
		}

		public override byte[] ReadAsBytes()
		{
			if (!Read() || t != JsonToken.Bytes)
				return null;
			return (byte[])v;
		}

		public override DateTime? ReadAsDateTime()
		{
			throw new NotImplementedException();
		}

		public override DateTimeOffset? ReadAsDateTimeOffset()
		{
			throw new NotImplementedException();
		}

		public override decimal? ReadAsDecimal()
		{
			throw new NotImplementedException();
		}

		public override int? ReadAsInt32()
		{
			// TODO: speed this up if needed
			if (!Read())
				return null;

			switch (t)
			{
				case JsonToken.Null:
					return null;
				case JsonToken.Integer:
				case JsonToken.Float:
					return Convert.ToInt32(v);
				case JsonToken.String:
					int i;
					if (int.TryParse(v.ToString(), out i))
						return i;
					else
						return null;
				default:
					return null;
			}
		}

		public override string ReadAsString()
		{
			if (!Read())
				return null;

			switch (t)
			{
				case JsonToken.Null:
					return null;
				case JsonToken.Float:
				case JsonToken.Integer:
				case JsonToken.Boolean:
				case JsonToken.String:
					return v.ToString();
				default:
					return null;
			}
		}
	}

	public class LBW : JsonWriter
	{
		private void WT(LBTOK t)
		{
			w.Write((byte)t);
		}

		public LBW(BinaryWriter w)
		{
			this.w = w;
		}
		private BinaryWriter w;

		public override void Flush()
		{
			w.Flush();
		}

		public override void Close()
		{
		}

		public override void WriteValue(bool value) { WT(value ? LBTOK.True : LBTOK.False); }

		public override void WriteValue(sbyte value) { WT(LBTOK.S8); w.Write(value); }
		public override void WriteValue(byte value) { WT(LBTOK.U8); w.Write(value); }
		public override void WriteValue(short value) { WT(LBTOK.S16); w.Write(value); }
		public override void WriteValue(ushort value) { WT(LBTOK.U16); w.Write(value); }
		public override void WriteValue(int value) { WT(LBTOK.S32); w.Write(value); }
		public override void WriteValue(uint value) { WT(LBTOK.U32); w.Write(value); }
		public override void WriteValue(long value) { WT(LBTOK.S64); w.Write(value); }
		public override void WriteValue(ulong value) { WT(LBTOK.U64); w.Write(value); }

		public override void WriteStartArray() { WT(LBTOK.StartArray); }
		public override void WriteEndArray() { WT(LBTOK.EndArray); }
		public override void WriteStartObject() { WT(LBTOK.StartObject); }
		public override void WriteEndObject() { WT(LBTOK.EndObject); }
		public override void WriteNull() { WT(LBTOK.Null); }
		public override void WriteUndefined() { WT(LBTOK.Undefined); }

		public override void WriteValue(float value) { WT(LBTOK.F32); w.Write(value); }
		public override void WriteValue(double value) { WT(LBTOK.F64); w.Write(value); }

		public override void WriteValue(byte[] value) { WT(LBTOK.ByteArray); w.Write(value.Length); w.Write(value); }

		public override void WriteComment(string text) { throw new NotImplementedException(); }
		public override void WriteWhitespace(string ws) { throw new NotImplementedException(); }
		protected override void WriteIndent() { throw new NotImplementedException(); }
		protected override void WriteIndentSpace() { throw new NotImplementedException(); }
		public override void WriteEnd() { throw new NotImplementedException(); }
		protected override void WriteEnd(JsonToken token) { throw new NotImplementedException(); }
		public override void WriteRaw(string json) { throw new NotImplementedException(); }
		public override void WriteRawValue(string json) { throw new NotImplementedException(); }
		public override void WriteStartConstructor(string name) { throw new NotImplementedException(); }
		public override void WriteEndConstructor() { throw new NotImplementedException(); }
		protected override void WriteValueDelimiter() { throw new NotImplementedException(); }

		public override void WritePropertyName(string name) { WT(LBTOK.Property); w.Write(name); }
		public override void WriteValue(string value) { WT(LBTOK.String); w.Write(value); }
		public override void WritePropertyName(string name, bool escape) { WT(LBTOK.Property); w.Write(name); } // no escaping required

		public override void WriteValue(char value) { throw new NotImplementedException(); }
		public override void WriteValue(DateTime value) { throw new NotImplementedException(); }
		public override void WriteValue(DateTimeOffset value) { throw new NotImplementedException(); }
		public override void WriteValue(decimal value) { throw new NotImplementedException(); }
		public override void WriteValue(Guid value) { throw new NotImplementedException(); }
		public override void WriteValue(TimeSpan value) { throw new NotImplementedException(); }
		public override void WriteValue(Uri value) { throw new NotImplementedException(); }
	}

}
