package ru.dbotthepony.kstarbound.defs.actor.player

import com.google.common.collect.ImmutableList
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
import ru.dbotthepony.kommons.gson.consumeNull
import ru.dbotthepony.kstarbound.defs.item.ItemDescriptor
import ru.dbotthepony.kstarbound.json.builder.JsonFactory

class BlueprintLearnList private constructor(private val tiers: Int2ObjectArrayMap<ImmutableList<Entry>>) {
	constructor(tiers: Map<Int, List<Entry>>) : this(Int2ObjectArrayMap<ImmutableList<Entry>>().also { for ((k, v) in tiers.entries) it.put(k, ImmutableList.copyOf(v)) })

	@JsonFactory
	data class Entry(val item: ItemDescriptor)

	operator fun get(tier: Int): List<Entry> {
		return tiers.getOrDefault(tier, ImmutableList.of())
	}

	companion object : TypeAdapterFactory {
		private val token = TypeToken.getParameterized(ImmutableList::class.java, Entry::class.java)

		override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
			if (type.rawType == BlueprintLearnList::class.java) {
				return object : TypeAdapter<BlueprintLearnList>() {
					private val listCodec = gson.getAdapter(token) as TypeAdapter<ImmutableList<Entry>>

					override fun write(out: JsonWriter, value: BlueprintLearnList?) {
						if (value == null)
							out.nullValue()
						else {
							out.beginObject()

							for ((tier, list) in value.tiers) {
								out.name("tier$tier")
								listCodec.write(out, list)
							}

							out.endObject()
						}
					}

					override fun read(`in`: JsonReader): BlueprintLearnList? {
						if (`in`.consumeNull())
							return null

						val tiers = Int2ObjectArrayMap<ImmutableList<Entry>>()

						`in`.beginObject()

						while (`in`.peek() != JsonToken.END_OBJECT) {
							val name = `in`.nextName()

							if (name.startsWith("tier")) {
								val tier = name.substring(4).toIntOrNull() ?: throw JsonSyntaxException("Invalid tier: $name")
								tiers[tier] = listCodec.read(`in`)
							} else {
								throw JsonSyntaxException("Invalid tier: $name")
							}
						}

						`in`.endObject()
						return BlueprintLearnList(tiers)
					}
				} as TypeAdapter<T>
			}

			return null
		}
	}
}
