package ru.dbotthepony.kstarbound.json.factory

import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableMap
import com.google.common.collect.ImmutableSet
import com.github.benmanes.caffeine.cache.Interner
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.internal.`$Gson$Types`
import com.google.gson.reflect.TypeToken

@Suppress("unchecked_cast")
class ImmutableCollectionAdapterFactory(val stringInterner: Interner<String> = Interner { it }) : TypeAdapterFactory {
	override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
		when (type.rawType) {
			ImmutableList::class.java -> {
				val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
				return ImmutableListTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
			}

			ImmutableSet::class.java -> {
				val elementType = `$Gson$Types`.getCollectionElementType(type.type, type.rawType)
				return ImmutableSetTypeAdapter(gson.getAdapter(TypeToken.get(elementType))) as TypeAdapter<T>
			}

			ImmutableMap::class.java -> {
				val (elementType0, elementType1) = `$Gson$Types`.getMapKeyAndValueTypes(type.type, type.rawType)

				if (`$Gson$Types`.getRawType(elementType0) == String::class.java) {
					return ImmutableMapTypeAdapter(stringInterner, gson.getAdapter(TypeToken.get(elementType1))) as TypeAdapter<T>
				}

				return ImmutableArrayMapTypeAdapter(
					gson.getAdapter(TypeToken.get(elementType0)),
					gson.getAdapter(TypeToken.get(elementType1))
				) as TypeAdapter<T>
			}

			else -> return null
		}
	}
}
