package ru.dbotthepony.mc.otm.client.screen.panels.input

import com.mojang.blaze3d.platform.InputConstants
import net.minecraft.client.gui.screens.Screen
import ru.dbotthepony.mc.otm.client.render.Widgets
import ru.dbotthepony.mc.otm.client.screen.panels.Dock
import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.RectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.HeightControls
import ru.dbotthepony.mc.otm.core.GetterSetter
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.Decimal

abstract class NumberInputPanel<out S : Screen, N : Number>(
	screen: S,
	parent: EditablePanel<*>,
	val prop: GetterSetter<N>,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = WIDTH,
	height: Float = HeightControls.BUTTON_HEIGHT * 2f,
	var min: N? = null,
	var max: N? = null,
) : EditablePanel<S>(screen, parent, x, y, width, height) {
	abstract fun toNumber(input: String): N?
	abstract fun increase(input: N): N
	abstract fun decrease(input: N): N
	abstract val isFractional: Boolean

	fun clamp(value: N): N {
		val min = min
		val max = max
		if (min != null && (value as Comparable<N>) < min) return min
		if (max != null && (value as Comparable<N>) > max) return max
		return value
	}

	fun increase() {
		prop.accept(clamp(increase(prop.get())))
	}

	fun decrease() {
		prop.accept(clamp(decrease(prop.get())))
	}

	val textInput = object : TextInputPanel<S>(screen, this@NumberInputPanel) {
		init {
			allowNumbersAndSign()
			dock = Dock.FILL
			text = prop.get().toString()
		}

		override fun acceptsCharacter(codepoint: Char, mods: Int, index: Int): Boolean {
			if (!isFractional && codepoint == '.') return false
			return super.acceptsCharacter(codepoint, mods, index)
		}

		override fun tickInner() {
			super.tickInner()

			if (!hasHierarchicalFocus()) {
				text = prop.get().toString()
			}
		}

		override fun onTextChanged(new: String, old: String) {
			if (hasHierarchicalFocus()) {
				val i = toNumber(new)
				if (i != null) prop.accept(i)
			}
		}
	}

	val controls = EditablePanel(screen, this, width = HeightControls.BUTTON_WIDTH, height = HeightControls.BUTTON_HEIGHT)
	val increaseControl = Control(true)
	val decreaseControl = Control(false)

	init {
		increaseControl.childrenOrder = 1
		decreaseControl.childrenOrder = 2
		textInput.dockRight = 2f
	}

	init {
		controls.dock = Dock.RIGHT
	}

	inner class Control(val isIncrease: Boolean) : RectangleButtonPanel<S>(screen, controls, width = HeightControls.BUTTON_WIDTH, height = HeightControls.BUTTON_HEIGHT) {
		override val PRESSED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_PRESSED else Widgets.ARROW_UP_BUTTON_PRESSED
		override val HOVERED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_HOVERED else Widgets.ARROW_UP_BUTTON_HOVERED
		override val IDLE = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_IDLE else Widgets.ARROW_UP_BUTTON_IDLE
		override val DISABLED = if (!isIncrease) Widgets.ARROW_DOWN_BUTTON_DISABLED else Widgets.ARROW_UP_BUTTON_DISABLED

		init {
			dock = Dock.TOP

			if (isIncrease)
				tooltips.add(TranslatableComponent("otm.gui.increase"))
			else
				tooltips.add(TranslatableComponent("otm.gui.decrease"))
		}

		override fun test(value: Int): Boolean {
			return value == InputConstants.MOUSE_BUTTON_LEFT || value == InputConstants.MOUSE_BUTTON_RIGHT
		}

		override fun onClick(mouseButton: Int) {
			if (mouseButton == InputConstants.MOUSE_BUTTON_LEFT) {
				if (isIncrease) {
					increase()
				} else {
					decrease()
				}
			} else if (mouseButton == InputConstants.MOUSE_BUTTON_RIGHT) {
				if (isIncrease) {
					decrease()
				} else {
					increase()
				}
			}
		}
	}

	companion object {
		const val WIDTH = 50f
	}
}

abstract class LNumberInputPanel<out S : Screen, N : Number>(
	screen: S,
	parent: EditablePanel<*>,
	prop: GetterSetter<N>,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = WIDTH,
	height: Float = HeightControls.BUTTON_HEIGHT * 2f,
	min: N? = null,
	max: N? = null,
	val toNumber: (String) -> N?,
	val increase: (N) -> N,
	val decrease: (N) -> N,
	final override val isFractional: Boolean
) : NumberInputPanel<S, N>(screen, parent, prop, x, y, width, height, min, max) {
	final override fun toNumber(input: String): N? {
		return toNumber.invoke(input)
	}

	final override fun increase(input: N): N {
		return increase.invoke(input)
	}

	final override fun decrease(input: N): N {
		return decrease.invoke(input)
	}
}

open class IntInputPanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>,
	prop: GetterSetter<Int>,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = WIDTH,
	height: Float = HeightControls.BUTTON_HEIGHT * 2f,
	var step: Int = 1,
	min: Int? = null,
	max: Int? = null,
) : NumberInputPanel<S, Int>(screen, parent, prop, x, y, width, height, min, max) {
	final override fun toNumber(input: String): Int? {
		return input.toIntOrNull()
	}

	final override fun increase(input: Int): Int {
		return input + step
	}

	final override fun decrease(input: Int): Int {
		return input - step
	}

	final override val isFractional: Boolean
		get() = false
}

open class DecimalInputPanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>,
	prop: GetterSetter<Decimal>,
	x: Float = 0f,
	y: Float = 0f,
	width: Float = WIDTH,
	height: Float = HeightControls.BUTTON_HEIGHT * 2f,
	var step: Decimal = Decimal.ONE,
	min: Decimal? = null,
	max: Decimal? = null,
) : NumberInputPanel<S, Decimal>(screen, parent, prop, x, y, width, height, min, max) {
	final override fun toNumber(input: String): Decimal? {
		return try {
			Decimal(input)
		} catch (err: NumberFormatException) {
			null
		}
	}

	final override fun increase(input: Decimal): Decimal {
		return input + step
	}

	final override fun decrease(input: Decimal): Decimal {
		return input - step
	}

	final override val isFractional: Boolean
		get() = true
}
