package ru.dbotthepony.kstarbound.defs.tile

import com.google.common.collect.ImmutableList
import ru.dbotthepony.kommons.math.RGBAColor
import ru.dbotthepony.kommons.util.Either
import ru.dbotthepony.kstarbound.Globals
import ru.dbotthepony.kstarbound.Registry
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.defs.IThingWithDescription
import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.json.builder.JsonFlat
import ru.dbotthepony.kstarbound.json.builder.JsonIgnore

@JsonFactory
data class TileDefinition(
	val materialId: Int,
	val materialName: String,
	val particleColor: RGBAColor? = null,
	val itemDrop: String? = null,
	val footstepSound: Either<ImmutableList<String>, String> = Either.left(ImmutableList.of()),
	val miningSounds: Either<ImmutableList<String>, String> = Either.left(ImmutableList.of()),

	val soil: Boolean = false,

	val category: String,

	@Deprecated("", replaceWith = ReplaceWith("this.actualDamageTable"))
	val damageTable: AssetReference<TileDamageConfig> = AssetReference(Globals::tileDamage),

	val health: Double? = null,
	val requiredHarvestLevel: Int? = null,

	val isConnectable: Boolean = true,

	@JsonFlat
	val descriptionData: ThingDescription,

	// meta tiles are treated uniquely by many systems
	// such as players/projectiles unable to break them, etc
	@JsonIgnore
	val isMeta: Boolean = false,

	val collisionKind: CollisionType = CollisionType.BLOCK,

	override val renderTemplate: AssetReference<RenderTemplate> = AssetReference(null),
	override val renderParameters: RenderParameters,

	val cascading: Boolean = false,
	val falling: Boolean = false,
	val foregroundOnly: Boolean = collisionKind != CollisionType.BLOCK,
	val supportsMods: Boolean = !(falling || cascading || collisionKind != CollisionType.BLOCK),
	val blocksLiquidFlow: Boolean = collisionKind.isSolidCollision,
) : IRenderableTile, IThingWithDescription by descriptionData {
	init {
		require(materialId > 0) { "Invalid tile ID $materialId" }
	}

	fun supportsModifier(modifier: Registry.Entry<TileModifierDefinition>): Boolean {
		return !isMeta && !modifier.value.isMeta && supportsMods
	}

	val actualDamageTable: TileDamageConfig by lazy {
		val dmg = damageTable.value ?: TileDamageConfig.EMPTY

		return@lazy if (health == null && requiredHarvestLevel == null) {
			dmg
		} else if (health != null && requiredHarvestLevel != null) {
			dmg.copy(totalHealth = health, harvestLevel = requiredHarvestLevel)
		} else if (health != null) {
			dmg.copy(totalHealth = health)
		} else {
			dmg.copy(harvestLevel = requiredHarvestLevel!!)
		}
	}
}
