package ru.dbotthepony.kstarbound.defs.tile

import com.google.common.collect.ImmutableMap
import it.unimi.dsi.fastutil.objects.ObjectArraySet
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.json.builder.JsonFactory

@JsonFactory
data class TileDamageConfig(
	val damageFactors: ImmutableMap<String, Double> = ImmutableMap.of(),
	val damageRecovery: Double = 1.0,
	val maximumEffectTime: Double = 1.5,
	val totalHealth: Double = 1.0,
	val harvestLevel: Int = 1,
) {
	val damageFactorsMapped: ImmutableMap<TileDamageType, Double> = damageFactors.entries.stream().map {
		var find = TileDamageType.entries.firstOrNull { e -> e.match(it.key) }

		if (find == null) {
			LOGGER.error("Unknown tile damage type ${it.key}!")
			find = TileDamageType.PROTECTED
		}

		find to it.value
	}.collect(ImmutableMap.toImmutableMap({ it.first }, { it.second }))

	fun damageDone(damage: TileDamage): Double {
		return (damageFactorsMapped[damage.type] ?: 1.0) * damage.amount
	}

	operator fun plus(other: TileDamageConfig): TileDamageConfig {
		val damageRecovery = damageRecovery + other.damageRecovery
		val maximumEffectTime = maximumEffectTime.coerceAtLeast(other.maximumEffectTime)
		val totalHealth = totalHealth + other.totalHealth
		val harvestLevel = harvestLevel.coerceAtLeast(other.harvestLevel)

		// TODO: in original code calculation of damage factors appears to be wrong
		// TODO: due to copy-paste error in for() loop argument
		// So this code is a little different
		val combinedKeys = ObjectArraySet<String>()
		combinedKeys.addAll(damageFactors.keys)
		combinedKeys.addAll(other.damageFactors.keys)

		val builder = ImmutableMap.Builder<String, Double>()

		for (key in combinedKeys) {
			if (key in damageFactors && key in other.damageFactors) {
				builder.put(key, totalHealth / ((this.totalHealth / (damageFactors[key] ?: 0.0) + other.totalHealth / (other.damageFactors[key] ?: 0.0))))
			} else {
				builder.put(key, damageFactors[key] ?: other.damageFactors[key]!!)
			}
		}

		return TileDamageConfig(builder.build(), damageRecovery, maximumEffectTime, totalHealth, harvestLevel)
	}

	companion object {
		val EMPTY = TileDamageConfig()

		private val LOGGER = LogManager.getLogger()
	}
}
