package ru.dbotthepony.mc.otm.matter

import com.google.common.collect.ImmutableList
import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.DecimalCodec
import ru.dbotthepony.mc.otm.data.simpleCodec

class UpdateAction(
	id: Either<ResourceLocation, TagKey<Item>>,
	matterFunctions: Collection<MatterFunction>,
	complexityFunctions: Collection<ComplexityFunction>,
	priorityFunctions: Collection<PriorityFunction>,
	errorOnFailure: Boolean = false,
) : AbstractRegistryAction(id, errorOnFailure) {
	data class MatterFunction(val function: IMatterFunction, val value: Decimal) {
		companion object {
			val CODEC: Codec<MatterFunction> by lazy {
				simpleCodec(::MatterFunction, MatterFunction::function, IMatterFunction.registry.codec, MatterFunction::value, DecimalCodec)
			}
		}
	}

	data class ComplexityFunction(val function: IMatterFunction, val value: Double)  {
		companion object {
			val CODEC: Codec<ComplexityFunction> by lazy {
				simpleCodec(::ComplexityFunction, ComplexityFunction::function, IMatterFunction.registry.codec, ComplexityFunction::value, Codec.DOUBLE)
			}
		}
	}

	data class PriorityFunction(val function: IMatterFunction, val value: Int)  {
		companion object {
			val CODEC: Codec<PriorityFunction> by lazy {
				simpleCodec(::PriorityFunction, PriorityFunction::function, IMatterFunction.registry.codec, PriorityFunction::value, Codec.INT)
			}
		}
	}

	companion object : Type<UpdateAction> {
		override val codec: Codec<UpdateAction> by lazy {
			RecordCodecBuilder.create {
				it.group(
					TargetCodec.fieldOf("id").forGetter(UpdateAction::id),
					Codec.list(MatterFunction.CODEC).fieldOf("matterFunctions").forGetter(UpdateAction::matterFunctions),
					Codec.list(ComplexityFunction.CODEC).fieldOf("complexityFunctions").forGetter(UpdateAction::complexityFunctions),
					Codec.list(PriorityFunction.CODEC).fieldOf("priorityFunctions").forGetter(UpdateAction::priorityFunctions),
					Codec.BOOL.optionalFieldOf("errorOnFailure", false).forGetter(UpdateAction::errorOnFailure)
				).apply(it, ::UpdateAction)
			}
		}
	}

	val matterFunctions: ImmutableList<MatterFunction> = ImmutableList.copyOf(matterFunctions)
	val complexityFunctions: ImmutableList<ComplexityFunction> = ImmutableList.copyOf(complexityFunctions)
	val priorityFunctions: ImmutableList<PriorityFunction> = ImmutableList.copyOf(priorityFunctions)

	override val type: Type<*>
		get() = Companion

	private fun apply(value: IMutableRegistryEntry, modifier: ResourceLocation) {
		matterFunctions.forEach {
			value.matter = it.function.updateValue(value.matter, it.value)
		}

		complexityFunctions.forEach {
			value.complexity = it.function.updateValue(value.complexity, it.value)
		}

		priorityFunctions.forEach {
			value.priority = it.function.updateValue(value.priority, it.value)
		}
	}

	override fun doUpdate(registry: MutableCollection<IMutableRegistryEntry>, source: ResourceLocation) {
		val iterator = registry.iterator()

		for (value in iterator) {
			if (value.name == id) {
				apply(value, source)
				return
			}
		}

		fail { "Could not find matter value with name $id" }
	}
}
