package ru.dbotthepony.kstarbound.defs.world

import com.google.common.collect.ImmutableMap
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import ru.dbotthepony.kstarbound.Globals
import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.defs.tile.TileDamageConfig
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.json.builder.JsonFlat

@JsonFactory
data class TreeVariant(
	val stemName: String,
	val foliageName: String,

	val stemDirectory: String,
	// may i fucking ask you why do you embed ENTIRE FUCKING FILE in
	// this struct, Chucklefuck???????
	val stemSettings: JsonElement,
	val stemHueShift: Double,

	val foliageDirectory: String,
	// AGAIN.
	val foliageSettings: JsonElement,
	val foliageHueShift: Double,

	val descriptions: ImmutableMap<String, String> = ImmutableMap.of(),
	val ceiling: Boolean,

	val ephemeral: Boolean,

	val stemDropConfig: JsonElement,
	val foliageDropConfig: JsonElement,

	val tileDamageParameters: TileDamageConfig,
) {
	@JsonFactory
	data class StemData(
		val name: String,
		val ceiling: Boolean = false,
		val dropConfig: JsonElement = JsonObject(),
		val shape: String,

		// TODO: original engine uses 'ephemeral' when creating tree variant with stem only,
		// TODO: and uses 'allowsBlockPlacement' when creating tree with stem and foliage
		// TODO: Bro what the FUCK?
		val ephemeral: Boolean = false,
		// val allowsBlockPlacement: Boolean = false,

		@JsonFlat
		val descriptions: ThingDescription,

		val damageTable: AssetReference<TileDamageConfig>? = null,
		val health: Double = 1.0,
	)

	@JsonFactory
	data class FoliageData(
		val name: String,
		val dropConfig: JsonElement = JsonObject(),
		val parallaxFoliage: Boolean = false,
		val shape: String,
	)

	companion object {
		fun create(stemName: String, stemHueShift: Double): TreeVariant {
			return create(Registries.treeStemVariants.getOrThrow(stemName), stemHueShift)
		}

		fun create(data: Registry.Entry<StemData>, stemHueShift: Double): TreeVariant {
			return TreeVariant(
				stemDirectory = data.file?.computeDirectory() ?: "/",
				stemSettings = data.json.deepCopy(),
				stemHueShift = stemHueShift,
				ceiling = data.value.ceiling,
				stemDropConfig = data.value.dropConfig.deepCopy(),
				descriptions = data.value.descriptions.fixDescription(data.key).toMap(),
				ephemeral = data.value.ephemeral,
				tileDamageParameters = (data.value.damageTable?.value ?: Globals.treeDamage).copy(totalHealth = data.value.health),

				foliageSettings = JsonObject(),
				foliageDropConfig = JsonObject(),
				foliageName = "",
				foliageDirectory = "/",
				foliageHueShift = 0.0,
				stemName = data.key,
			)
		}

		fun create(stemName: String, stemHueShift: Double, foliageName: String, foliageHueShift: Double): TreeVariant {
			val data = Registries.treeStemVariants.getOrThrow(stemName)
			val fdata = Registries.treeFoliageVariants.getOrThrow(foliageName)

			return create(data, stemHueShift, fdata, foliageHueShift)
		}

		fun create(data: Registry.Entry<StemData>, stemHueShift: Double, fdata: Registry.Entry<FoliageData>, foliageHueShift: Double): TreeVariant {
			return TreeVariant(
				stemDirectory = data.file?.computeDirectory() ?: "/",
				stemSettings = data.json.deepCopy(),
				stemHueShift = stemHueShift,
				ceiling = data.value.ceiling,
				stemDropConfig = data.value.dropConfig.deepCopy(),
				descriptions = data.value.descriptions.fixDescription("${data.key} with ${fdata.key}").toMap(),
				ephemeral = data.value.ephemeral,
				tileDamageParameters = (data.value.damageTable?.value ?: Globals.treeDamage).copy(totalHealth = data.value.health),

				foliageSettings = fdata.json,
				foliageDropConfig = fdata.value.dropConfig.deepCopy(),
				foliageName = fdata.key,
				foliageDirectory = fdata.file?.computeDirectory() ?: "/",
				foliageHueShift = foliageHueShift,
				stemName = data.key,
			)
		}
	}
}
