package ru.dbotthepony.mc.otm.android

import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.ChatFormatting
import net.minecraft.network.chat.Component
import net.minecraftforge.eventbus.api.IEventBus
import net.minecraftforge.registries.DeferredRegister
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.config.AndroidConfig
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.getValue
import ru.dbotthepony.mc.otm.core.util.formatPower
import ru.dbotthepony.mc.otm.data.ComponentCodec
import ru.dbotthepony.mc.otm.data.SingletonCodec
import ru.dbotthepony.mc.otm.data.simpleCodec
import ru.dbotthepony.mc.otm.registry.RegistryDelegate

object AndroidResearchDescriptions {
	private val registrar = DeferredRegister.create(AndroidResearchDescription.registryKey, OverdriveThatMatters.MOD_ID)

	init {
		registrar.register("plain") { PlainAndroidResearchDescription }
	}

	internal fun register(bus: IEventBus) {
		registrar.register(bus)
	}

	val ENDER_TELEPORTER: AndroidResearchDescription.Singleton by registrar.register("ender_teleporter") {
		AndroidResearchDescription.singleton {
			TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.EnderTeleporter.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
	}

	val FALL_DAMPENERS: AndroidResearchDescription.Leveled by registrar.register("fall_dampeners") {
		AndroidResearchDescription.Leveled { _, list, level ->
			list.add(TranslatableComponent("otm.fall_dampeners.description",
				TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL_P * level).toFloat().coerceIn(0f, 1f) * 100f)).withStyle(ChatFormatting.YELLOW),
				TextComponent("%.1f".format((AndroidConfig.FALL_DAMAGE_REDUCTION_PER_LEVEL_F * level).toFloat())).withStyle(ChatFormatting.YELLOW),
			)) }
	}

	val ITEM_MAGNET: AndroidResearchDescription.Singleton by registrar.register("item_magnet") {
		AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_tick", AndroidConfig.Magnet.POWER_DRAW.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
	}

	val JUMP_BOOST: AndroidResearchDescription.Singleton by registrar.register("jump_boost") {
		AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.JumpBoost.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
	}

	val SHOCKWAVE: AndroidResearchDescription.Singleton by registrar.register("shockwave") {
		AndroidResearchDescription.singleton { TranslatableComponent("otm.gui.power_cost_per_use", AndroidConfig.Shockwave.ENERGY_COST.formatPower(formatAsReadable = ShiftPressedCond).copy().withStyle(ChatFormatting.YELLOW)) }
	}
}

/**
 * Instance, representing ready to use description populator
 */
interface AndroidResearchDescription {
	/**
	 * Type, representing raw description populator in registry
	 */
	interface Type<T : AndroidResearchDescription> {
		val codec: Codec<T>
	}

	abstract class Singleton : AndroidResearchDescription, Type<Singleton> {
		override val codec = SingletonCodec(this)
		override val type: Type<*>
			get() = this
	}

	class Leveled(val callback: (research: AndroidResearch, lines: MutableList<Component>, level: Int) -> Unit) : Type<Leveled.Instance> {
		inner class Instance(val level: Int) : AndroidResearchDescription {
			override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
				callback.invoke(research, lines, level)
			}

			override val type: Type<*>
				get() = this@Leveled
		}

		override val codec: Codec<Instance> by lazy {
			RecordCodecBuilder.create {
				it.group(Codec.INT.fieldOf("level").forGetter(Instance::level)).apply(it, ::Instance)
			}
		}
	}

	fun addLines(research: AndroidResearch, lines: MutableList<Component>)
	val type: Type<*>

	companion object {
		private val delegate = RegistryDelegate<Type<*>>("android_research_description") {
			disableSaving()
		}

		val registry by delegate
		val registryKey get() = delegate.key

		val CODEC: Codec<AndroidResearchDescription> by lazy {
			registry.codec.dispatch({ it.type }, { it.codec })
		}

		internal fun register(bus: IEventBus) {
			bus.addListener(delegate::build)
		}

		fun singleton(callback: (research: AndroidResearch) -> Component): Singleton {
			return object : Singleton() {
				override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
					lines.add(callback.invoke(research))
				}
			}
		}
	}
}

object PlainAndroidResearchDescription : AndroidResearchDescription.Type<PlainAndroidResearchDescription.Instance> {
	data class Instance(val line: Component) : AndroidResearchDescription {
		override fun addLines(research: AndroidResearch, lines: MutableList<Component>) {
			lines.add(line.copy())
		}

		override val type: PlainAndroidResearchDescription
			get() = PlainAndroidResearchDescription
	}

	fun make(line: Component) = Instance(line)

	override val codec: Codec<Instance> by lazy {
		Codec
			.either(ComponentCodec, simpleCodec(::Instance, Instance::line, ComponentCodec))
			.xmap({ c -> c.map({ Instance(it) }, { it }) }, { c -> Either.left(c.line) })
	}
}
