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

import com.mojang.blaze3d.platform.InputConstants
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.narration.NarratableEntry
import net.minecraft.client.gui.narration.NarratableEntry.NarrationPriority
import net.minecraft.client.gui.narration.NarrationElementOutput
import net.minecraft.client.gui.screens.Screen
import net.minecraft.network.chat.Component
import org.lwjgl.opengl.GL30
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.AbstractButtonPanel
import ru.dbotthepony.mc.otm.core.TranslatableComponent

open class FramePanel<out S : Screen>(
	screen: S,
	parent: EditablePanel<*>?,
	x: Float,
	y: Float,
	width: Float,
	height: Float,
	var title: Component? = null,
) : EditablePanel<S>(screen, parent, x, y, width, height), NarratableEntry {
	constructor(screen: S, width: Float, height: Float, title: Component?) : this(screen, null, 0f, 0f, width, height, title)

	open inner class Tab(
		var onOpen: Runnable? = null,
		var onClose: Runnable? = null,
		var activeIcon: IGUIRenderable? = null,
		var inactiveIcon: IGUIRenderable? = null,
	) : AbstractButtonPanel<S>(this@FramePanel.screen, this@FramePanel, 0f, 0f, 28f, 28f) {
		var isActive = tabs.isEmpty()
		var initial = false

		init {
			tabs.add(this)
		}

		fun showHidePanels(input: List<EditablePanel<*>>) {
			onOpen = Runnable {
				for (child in input) {
					child.visible = true
				}
			}

			onClose = Runnable {
				for (child in input) {
					child.visible = false
				}
			}
		}

		override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
			val width: Float
			val height: Float

			if (isActive) {
				width = TAB_WIDTH_ACTIVE
				height = TAB_HEIGHT_ACTIVE
				RECTANGLE.middle.render(graphics, 2f, 2f, width - 4, height - 2)
			} else {
				width = TAB_WIDTH
				height = TAB_HEIGHT
				TAB_BACKGROUND.render(graphics, 2f, 2f, width - 4, height - 2)
			}

			RECTANGLE.top.render(graphics, 3f, 0f, width = width - 6)
			RECTANGLE.left.render(graphics, 0f, 3f, height = (height - if (isActive) if (initial) 2 else 4 else 3))

			RECTANGLE.right.render(graphics, width - RECTANGLE.right.width, 3f, height = (height - if (isActive) 4 else 3))

			RECTANGLE.topLeft.render(graphics, 0f, 0f)
			RECTANGLE.topRight.render(graphics, width - RECTANGLE.topRight.width, 0f)

			if (isActive) {
				if (!initial) {
					TAB_LEFT_CONNECTION.render(graphics, 0f, height - TAB_LEFT_CONNECTION.height - 1)
				}

				TAB_RIGHT_CONNECTION.render(
					graphics,
					width - TAB_RIGHT_CONNECTION.width,
					height - TAB_LEFT_CONNECTION.height - 1
				)

				val skinActive = activeIcon ?: return
				skinActive.render(graphics, TAB_WIDTH_ACTIVE / 2f, TAB_HEIGHT_ACTIVE / 2f, gravity = RenderGravity.CENTER_CENTER)
			} else {
				val skinInactive = inactiveIcon ?: return
				skinInactive.render(graphics, width / 2f, TAB_HEIGHT_ACTIVE / 2f, gravity = RenderGravity.CENTER_CENTER)
			}
		}

		protected open fun onOpen() {
			onOpen?.run()
		}

		protected open fun onClose() {
			onClose?.run()
		}

		override fun onClick(mouseButton: Int) {
			if (!isActive) {
				for (tab in tabs) {
					if (tab !== this) {
						if (tab.isActive) {
							tab.onClose()
							tab.isActive = false
						}
					}
				}

				isActive = true
				onOpen()
			}
		}
	}

	inner class CloseButton : AbstractButtonPanel<S>(screen, this@FramePanel, this@FramePanel.width - CLOSE_BUTTON.width, 0f, CLOSE_BUTTON.width, CLOSE_BUTTON.height) {
		override fun onClick(mouseButton: Int) {
			close()
		}

		override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
			if (isPressed) {
				CLOSE_BUTTON_PRESSED.render(graphics, 0f, 0f, width, height)
			} else if (isHovered) {
				CLOSE_BUTTON_HOVERED.render(graphics, 0f, 0f, width, height)
			} else {
				CLOSE_BUTTON.render(graphics, 0f, 0f, width, height)
			}
		}

		override fun onRemoved() {
			super.onRemoved()

			if (closeButton == this) {
				closeButton = null
			}
		}
	}

	inner class HelpButton : EditablePanel<S>(screen, this@FramePanel, this@FramePanel.width - HELP_BUTTON.width - CLOSE_BUTTON.width, 0f, HELP_BUTTON.width, HELP_BUTTON.height) {
		// var isActive = false

		/*override fun onClick(mouseButton: Int) {
			isActive = !isActive
		}*/

		override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
			/*if (isPressed) {
				HELP_BUTTON_PRESSED.render(graphics, 0f, 0f, width, height)
			} else*/ if (isHovered /*|| isActive */) {
				HELP_BUTTON_HOVERED.render(graphics, 0f, 0f, width, height)
			} else {
				HELP_BUTTON.render(graphics, 0f, 0f, width, height)
			}
		}

		/*override val needsPostRender: Boolean
			get() = isActive

		override fun innerRenderPost(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
			val tooltip = tooltip
			val tooltipList = tooltipList

			val list = tooltipList ?: tooltip?.let { listOf(it) } ?: listOf()

			if (list.isNotEmpty()) {
				graphics.renderComponentTooltip(
					font, list,
					mouseX.toInt(), mouseY.toInt()
				)
			}
		}*/

		/*override fun shouldRenderTooltips(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float): Boolean {
			return super.shouldRenderTooltips(graphics, mouseX, mouseY, partialTick) && !isActive
		}*/

		fun addSlotFiltersHelp(): HelpButton {
			tooltips.add(TranslatableComponent("otm.gui.help.slot_filters"))
			return this
		}

		override fun onRemoved() {
			super.onRemoved()

			if (helpButton == this) {
				helpButton = null
			}
		}
	}

	protected val tabs: java.util.ArrayList<Tab> = ArrayList()

	override fun performLayout() {
		for ((i, tab) in tabs.withIndex()) {
			tab.setPos(i * TAB_WIDTH, -TAB_HEIGHT)
			tab.initial = i == 0
		}

		closeButton?.setPos(width - CLOSE_BUTTON.width, 0f)
		helpButton?.setPos(width - HELP_BUTTON.width - CLOSE_BUTTON.width, 0f)

		super.performLayout()
	}

	protected var dragging = false

	var closeButton: CloseButton? = null
		protected set
	var helpButton: HelpButton? = null
		protected set

	var closeOnEscape = false

	fun makeCloseButton(): CloseButton {
		if (closeButton == null)
			closeButton = CloseButton()

		return closeButton!!
	}

	fun makeHelpButton(): HelpButton {
		if (helpButton == null)
			helpButton = HelpButton()

		return helpButton!!
	}

	fun makeHelpButton(tooltips: Collection<Component>): HelpButton {
		if (helpButton == null) {
			helpButton = HelpButton()
			helpButton!!.tooltips.addAll(tooltips)
		}

		return helpButton!!
	}

	/**
	 * Adds close button and makes panel [close] when user presses ESC with this panel focused
	 */
	fun behaveAsWindow() {
		closeOnEscape = true
		makeCloseButton()
	}

	init {
		setDockPadding(PADDING, if (title != null) PADDING_TOP else PADDING, PADDING, PADDING)
	}

	override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
		val (_, y1) = screenToLocal(x, y)

		if (parent == null && y1 >= 0 && y1 <= 12 && tryToGrabMouseInput()) {
			dragging = true
			return true
		}

		return super.mouseClickedInner(x, y, button)
	}

	override fun mouseReleasedInner(x: Double, y: Double, button: Int): Boolean {
		if (dragging) {
			grabMouseInput = false
			dragging = false
			return true
		}

		return super.mouseReleasedInner(x, y, button)
	}

	override fun mouseDraggedInner(x: Double, y: Double, button: Int, xDelta: Double, yDelta: Double): Boolean {
		if (dragging) {
			val oldX = this.x
			val oldY = this.y

			if (canDrag(oldX, oldY, this.x + xDelta.toFloat(), this.y + yDelta.toFloat(), xDelta.toFloat(), yDelta.toFloat())) {
				this.x += xDelta.toFloat()
				this.y += yDelta.toFloat()

				onDragged(oldX, oldY, this.x, this.y, xDelta.toFloat(), yDelta.toFloat())
			}
		}

		return super.mouseDraggedInner(x, y, button, xDelta, yDelta)
	}

	protected open fun canDrag(oldX: Float, oldY: Float, newX: Float, newY: Float, dragX: Float, dragY: Float): Boolean {
		return true
	}

	protected open fun onDragged(oldX: Float, oldY: Float, newX: Float, newY: Float, dragX: Float, dragY: Float) {

	}

	protected val closeCallbacks = ArrayList<Runnable>()

	fun onClose(callback: Runnable) {
		closeCallbacks.add(callback)
	}

	/**
	 * Usually just calls [remove]
	 */
	open fun close() {
		if (isRemoved) return
		remove()
		closeCallbacks.forEach { it.run() }
	}

	override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean {
		if (key == InputConstants.KEY_ESCAPE && closeOnEscape) {
			close()
			return true
		}

		return super.keyPressedInternal(key, scancode, mods)
	}

	override fun innerRender(graphics: GuiGraphics, mouseX: Float, mouseY: Float, partialTick: Float) {
		RECTANGLE.render(graphics, width = width, height = height)

		// title
		val title = title ?: return
		RenderSystem.depthFunc(GL30.GL_ALWAYS)
		graphics.drawAligned(font, title, RenderGravity.TOP_LEFT, 8f, 5f, 4210752)
		RenderSystem.depthFunc(GL30.GL_ALWAYS)
	}

	override fun narrationPriority(): NarrationPriority {
		return NarrationPriority.NONE
	}

	override fun updateNarration(narrationElementOutput: NarrationElementOutput) {}

	fun toScreenCenter() {
		setPos((screen.width / 2 - (width / 2).toInt()).toFloat(), (screen.height / 2 - (height / 2).toInt()).toFloat())
	}

	companion object {
		fun <S : Screen> padded(
			screen: S,
			parent: EditablePanel<*>?,
			x: Float,
			y: Float,
			width: Float,
			height: Float,
			title: Component? = null,
		) = FramePanel(screen, parent, x, y, width + PADDING * 2, height + PADDING_TOP + PADDING, title)

		fun <S : Screen> padded(
			screen: S,
			parent: EditablePanel<*>?,
			width: Float,
			height: Float,
			title: Component? = null,
		) = FramePanel(screen, parent, 0f, 0f, width + PADDING * 2, height + PADDING_TOP + PADDING, title)

		fun <S : Screen> padded(
			screen: S,
			width: Float,
			height: Float,
			title: Component? = null,
		) = FramePanel(screen, null, 0f, 0f, width + PADDING * 2, height + PADDING_TOP + PADDING, title)

		const val PADDING = 8f
		const val PADDING_TOP = 14f

		val RECTANGLE = StretchingRectangleElement(
			topLeft = WidgetLocation.MISC.sprite(x = 18f, y = 0f, width = 6f, height = 6f),
			topRight = WidgetLocation.MISC.sprite(x = 24f, y = 0f, width = 6f, height = 6f),
			left = WidgetLocation.MISC.sprite(x = 18f, y = 4f, width = 3f, height = 5f),
			right = WidgetLocation.MISC.sprite(x = 25f, y = 3f, width = 5f, height = 5f),
			top = WidgetLocation.MISC.sprite(x = 22f, y = 0f, width = 5f, height = 3f),
			bottomLeft = WidgetLocation.MISC.sprite(x = 18f, y = 6f, width = 6f, height = 6f),
			bottomRight = WidgetLocation.MISC.sprite(x = 24f, y = 6f, width = 6f, height = 6f),
			bottom = WidgetLocation.MISC.sprite(x = 21f, y = 9f, width = 5f, height = 3f),
			middle = WidgetLocation.MISC.sprite(x = 30f, y = 12f, width = 6f, height = 6f),
			padding = DockProperty(-3f, -3f, -3f, -3f)
		)

		val TAB_RIGHT_CONNECTION 	= WidgetLocation.MISC.sprite(x = 30f, y = 0f,  width = 3f,  height = 5f)
		val TAB_LEFT_CONNECTION 	= WidgetLocation.MISC.sprite(x = 33f, y = 0f,  width = 3f,  height = 5f)
		val TAB_BACKGROUND 			= WidgetLocation.MISC.sprite(x = 30f, y = 6f,  width = 6f,  height = 6f)

		val CLOSE_BUTTON 			= WidgetLocation.MISC.sprite(x = 51f, y = 0f,  width = 13f, height = 14f)
		val CLOSE_BUTTON_HOVERED	= WidgetLocation.MISC.sprite(x = 51f, y = 14f, width = 13f, height = 14f)
		val CLOSE_BUTTON_PRESSED	= WidgetLocation.MISC.sprite(x = 51f, y = 28f, width = 13f, height = 14f)

		val HELP_BUTTON 			= WidgetLocation.MISC.sprite(x = 40f, y = 0f,  width = 11f, height = 14f)
		val HELP_BUTTON_HOVERED		= WidgetLocation.MISC.sprite(x = 40f, y = 14f, width = 11f, height = 14f)
		val HELP_BUTTON_PRESSED		= WidgetLocation.MISC.sprite(x = 40f, y = 28f, width = 11f, height = 14f)

		const val TAB_HEIGHT = 28f
		const val TAB_WIDTH = 28f
		const val TAB_HEIGHT_ACTIVE = 32f
		const val TAB_WIDTH_ACTIVE = 28f
	}
}
