package ru.dbotthepony.kstarbound.json.factory

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.github.benmanes.caffeine.cache.Interner
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kommons.gson.consumeNull

class ImmutableMapTypeAdapter<V>(val stringInterner: Interner<String>, val elementAdapter: TypeAdapter<V>) : TypeAdapter<ImmutableMap<String, V>>() {
	override fun write(out: JsonWriter, value: ImmutableMap<String, V>?) {
		if (value == null) {
			out.nullValue()
			return
		}

		out.beginObject()

		for ((k, v) in value) {
			out.name(k)
			elementAdapter.write(out, v)
		}

		out.endObject()
	}

	override fun read(reader: JsonReader): ImmutableMap<String, V>? {
		if (reader.consumeNull())
			return null

		if (reader.peek() == JsonToken.BEGIN_ARRAY) {
			val builder = ImmutableMap.Builder<String, V>()

			while (reader.peek() !== JsonToken.END_ARRAY) {
				builder.put(
					stringInterner.intern(reader.nextString()),
					elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls are not allowed, near ${reader.path}")
				)
			}

			return builder.build()
		}

		reader.beginObject()

		val builder = ImmutableMap.Builder<String, V>()

		while (reader.peek() !== JsonToken.END_OBJECT) {
			builder.put(stringInterner.intern(reader.nextName()), elementAdapter.read(reader) ?: throw JsonSyntaxException("Nulls are not allowed, near ${reader.path}"))
		}

		reader.endObject()
		return builder.build()
	}
}
