package ru.dbotthepony.kstarbound.world.terrain

import com.google.common.collect.ImmutableList
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import ru.dbotthepony.kommons.gson.get
import ru.dbotthepony.kstarbound.defs.world.TerrainSelectorParameters
import ru.dbotthepony.kstarbound.json.builder.JsonFactory

sealed class ComparingTerrainSelector(data: Data, parameters: TerrainSelectorParameters) : AbstractTerrainSelector<ComparingTerrainSelector.Data>(data, parameters) {
	@JsonFactory
	data class Data(val sources: ImmutableList<JsonElement>)

	protected val sources = ArrayList<AbstractTerrainSelector<*>>()

	init {
		require(data.sources.isNotEmpty()) { "'sources' array is empty" }

		for (source in data.sources) {
			sources.add(TerrainSelectorType.create(source, parameters.withSeed(parameters.seed + if (source is JsonObject) source.get("seedBias", 0L) else 0L)))
		}
	}

	class Max(data: Data, parameters: TerrainSelectorParameters) : ComparingTerrainSelector(data, parameters) {
		constructor(parameters: TerrainSelectorParameters, vararg sources: AbstractTerrainSelector<*>) : this(
			Data(ImmutableList.copyOf(sources.map { it.toJsonPlain() })),
			parameters
		)

		override fun get(x: Int, y: Int): Double {
			return sources.maxOf { it[x, y] }
		}

		override val type: TerrainSelectorType
			get() = TerrainSelectorType.MAX
	}

	class Min(data: Data, parameters: TerrainSelectorParameters) : ComparingTerrainSelector(data, parameters) {
		constructor(parameters: TerrainSelectorParameters, vararg sources: AbstractTerrainSelector<*>) : this(
			Data(ImmutableList.copyOf(sources.map { it.toJsonPlain() })),
			parameters
		)

		override fun get(x: Int, y: Int): Double {
			return sources.minOf { it[x, y] }
		}

		override val type: TerrainSelectorType
			get() = TerrainSelectorType.MIN
	}

	class MinMax(data: Data, parameters: TerrainSelectorParameters) : ComparingTerrainSelector(data, parameters) {
		constructor(parameters: TerrainSelectorParameters, vararg sources: AbstractTerrainSelector<*>) : this(
			Data(ImmutableList.copyOf(sources.map { it.toJsonPlain() })),
			parameters
		)

		override fun get(x: Int, y: Int): Double {
			var value = 0.0

			for (source in sources) {
				val sample = source[x, y]

				if (value > 0.0 || sample > 0.0)
					value = value.coerceAtLeast(sample)
				else
					value = value.coerceAtMost(sample)
			}

			return value
		}

		override val type: TerrainSelectorType
			get() = TerrainSelectorType.MIN_MAX
	}
}
