package ru.dbotthepony.mc.otm.capability.matter

import net.minecraft.world.item.Item
import net.minecraftforge.common.capabilities.ICapabilityProvider
import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.orNull
import java.util.*
import java.util.function.Predicate
import java.util.stream.Collectors
import java.util.stream.Stream
import kotlin.math.roundToInt

interface IPatternStorage {
	/**
	 * It must return new stream each time
	 */
	val patterns: Stream<out IPatternState>

	fun findPatterns(item: Item): Collection<IPatternState> {
		return findPatterns { item == it.item }
	}

	fun findPatterns(predicate: Predicate<IPatternState>): Collection<IPatternState> {
		return patterns.filter(predicate).collect(Collectors.toList())
	}

	fun findPattern(item: Item): IPatternState? {
		return patterns.filter { it.item == item }.findAny().orElse(null)
	}

	fun getPattern(id: UUID?): IPatternState? {
		return patterns.filter { it.id == id }.findAny().orElse(null)
	}

	fun hasPattern(item: Item): Boolean {
		return patterns.filter { it.item == item }.findAny().isPresent
	}

	fun hasPattern(state: IPatternState): Boolean {
		return hasPattern(state.id)
	}

	fun hasPattern(id: UUID?): Boolean {
		return patterns.filter { it.id == id }.findAny().isPresent
	}

	val patternCapacity: Int
	val storedPatterns: Int

	/**
	 * Prefer way to call it is to first call only_update = true
	 * and if it fail, try only_update = false
	 *
	 * @param pattern pattern to be inserted or update value from
	 * @param onlyUpdate do not insert new pattern if this pattern's UUID is not matched
	 * @param simulate whenever to affect state
	 * @return record of status of the operation (at status() FAIL, UPDATED, INSERTED) as well as new_state and old_state
	 */
	fun insertPattern(pattern: IPatternState, onlyUpdate: Boolean, simulate: Boolean): PatternInsertStatus

	fun insertPattern(pattern: IPatternState, simulate: Boolean): PatternInsertStatus {
		return insertPattern(pattern, false, simulate)
	}

	fun updatePattern(pattern: IPatternState, simulate: Boolean): PatternInsertStatus {
		return insertPattern(pattern, true, simulate)
	}
}

fun IPatternStorage.getBarWidth(): Int {
	return ((storedPatterns.toFloat() / patternCapacity.toFloat()).coerceAtLeast(0f).coerceAtMost(1f) * 13f).roundToInt()
}

fun IPatternStorage.getBarColor(): Int {
	return RGBAColor.LOW_PATTERNS.linearInterpolation((storedPatterns / patternCapacity).toFloat(), RGBAColor.FULL_PATTERNS).toRGB()
}

val ICapabilityProvider.patterns: IPatternStorage? get() = getCapability(MatteryCapability.PATTERN).orNull()
