package ru.dbotthepony.kstarbound.json

import com.google.gson.JsonArray
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonPrimitive
import ru.dbotthepony.kommons.io.writeBinaryString
import ru.dbotthepony.kommons.io.writeSignedVarLong
import ru.dbotthepony.kommons.io.writeVarInt
import java.io.DataOutputStream

fun DataOutputStream.writeJsonElement(value: JsonElement) {
	when (value) {
		is JsonNull -> write(BinaryJsonReader.TYPE_NULL)
		is JsonPrimitive -> {
			if (value.isNumber) {
				when (val v = value.asNumber) {
					is Float, is Double -> {
						write(BinaryJsonReader.TYPE_DOUBLE)
						writeDouble(v.toDouble())
					}

					is Int, is Long, is Byte, is Short -> {
						write(BinaryJsonReader.TYPE_INT)
						writeSignedVarLong(v.toLong())
					}

					else -> {
						// throw IllegalArgumentException("Unknown number type: ${v::class} ($v)")
						// hi com.google.gson.internal.LazilyParsedNumber
						val doubleBits by lazy { v.toDouble() }

						if (v.toString().contains('.') || doubleBits % 1.0 != 0.0) {
							write(BinaryJsonReader.TYPE_DOUBLE)
							writeDouble(doubleBits)
						} else {
							write(BinaryJsonReader.TYPE_INT)
							writeSignedVarLong(v.toLong())
						}
					}
				}
			} else if (value.isString) {
				write(BinaryJsonReader.TYPE_STRING)
				writeBinaryString(value.asString)
			} else if (value.isBoolean) {
				write(BinaryJsonReader.TYPE_BOOLEAN)
				write(if (value.asBoolean) 1 else 0)
			} else {
				throw IllegalArgumentException("Unknown primitive value type: $value")
			}
		}

		is JsonObject -> {
			write(BinaryJsonReader.TYPE_OBJECT)
			writeJsonObject(value)
		}

		is JsonArray -> {
			write(BinaryJsonReader.TYPE_ARRAY)
			writeJsonArray(value)
		}
	}
}

fun DataOutputStream.writeJsonObject(value: JsonObject) {
	writeVarInt(value.size())

	for ((k, v) in value.entrySet()) {
		writeBinaryString(k)
		writeJsonElement(v)
	}
}

fun DataOutputStream.writeJsonArray(value: JsonArray) {
	writeVarInt(value.size())

	for (v in value) {
		writeJsonElement(v)
	}
}
