package ru.dbotthepony.kstarbound.json

import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.annotations.JsonAdapter
import com.google.gson.reflect.TypeToken

object JsonAdapterTypeFactory : TypeAdapterFactory {
	private fun <T : Any?> wrap(input: Any, annotation: JsonAdapter): TypeAdapter<T> {
		input as TypeAdapter<T>

		if (annotation.nullSafe) {
			return NullSafeTypeAdapter(input)
		} else {
			return input
		}
	}

	override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
		val annotation = type.rawType.getAnnotation(JsonAdapter::class.java) ?: return null

		val constructor0 = try {
			annotation.value.java.getDeclaredConstructor(Gson::class.java, TypeToken::class.java)
		} catch (err: NoSuchMethodException) {
			null
		}

		if (constructor0 != null) {
			return wrap(constructor0.newInstance(gson, type), annotation)
		}

		val constructor1 = try {
			annotation.value.java.getDeclaredConstructor(Gson::class.java)
		} catch (err: NoSuchMethodException) {
			null
		}

		if (constructor1 != null) {
			return wrap(constructor1.newInstance(gson), annotation)
		}

		val constructor2 = try {
			annotation.value.java.getDeclaredConstructor()
		} catch (err: NoSuchMethodException) {
			null
		}

		if (constructor2 != null) {
			return wrap(constructor2.newInstance(), annotation)
		}

		throw RuntimeException("${type.rawType} has @JsonAdapter annotation, but it doesn't reference legal class ${annotation.value}")
	}
}
