package ru.dbotthepony.kstarbound.json

import com.google.gson.Gson
import com.google.gson.JsonElement
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.bind.JsonTreeReader
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import java.io.StringReader

interface ContextualizedTypeAdapter<T> {
	fun write(writer: JsonWriter, value: T, gson: Gson)

	fun read(reader: JsonReader, gson: Gson): T

	fun read(value: String, gson: Gson): T {
		return read(JsonReader(StringReader(value)), gson)
	}

	fun read(value: JsonElement, gson: Gson): T {
		return read(JsonTreeReader(value), gson)
	}
}

inline fun <reified T> ContextualizedTypeAdapter<T>.factory() = ContextualizedTypeAdapterWrapper(T::class.java, this)

class ContextualizedTypeAdapterWrapper<T>(val type: Class<T>, val adapter: ContextualizedTypeAdapter<T>) : TypeAdapterFactory {
	override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
		if (this.type.isAssignableFrom(type.rawType)) {
			return Adapter(gson) as TypeAdapter<T>
		}

		return null
	}

	private inner class Adapter(private val gson: Gson) : TypeAdapter<T>() {
		override fun write(out: JsonWriter, value: T) {
			return adapter.write(out, value, gson)
		}

		override fun read(`in`: JsonReader): T {
			return adapter.read(`in`, gson)
		}
	}
}
