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

import net.minecraft.ChatFormatting
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.EditBox
import net.minecraft.network.chat.Component
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.capability.matter.IPatternState
import ru.dbotthepony.mc.otm.capability.matter.IReplicationTask
import ru.dbotthepony.mc.otm.client.ShiftPressedCond
import ru.dbotthepony.mc.otm.client.render.*
import ru.dbotthepony.mc.otm.client.screen.MatteryScreen
import ru.dbotthepony.mc.otm.client.screen.panels.*
import ru.dbotthepony.mc.otm.client.screen.panels.button.ButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.DeviceControls
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeBooleanRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.button.LargeEnumRectangleButtonPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.EditBoxPanel
import ru.dbotthepony.mc.otm.client.screen.panels.input.TextInputPanel
import ru.dbotthepony.mc.otm.client.screen.panels.slot.AbstractSlotPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.DiscreteScrollBarPanel
import ru.dbotthepony.mc.otm.client.screen.panels.util.ScrollBarConstants
import ru.dbotthepony.mc.otm.core.TextComponent
import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.math.RGBAColor
import ru.dbotthepony.mc.otm.core.math.integerDivisionDown
import ru.dbotthepony.mc.otm.core.util.ItemSorter
import ru.dbotthepony.mc.otm.core.util.formatMatter
import ru.dbotthepony.mc.otm.core.util.formatTickDuration
import ru.dbotthepony.mc.otm.matter.MatterManager
import ru.dbotthepony.mc.otm.menu.matter.MatterPanelMenu
import ru.dbotthepony.mc.otm.menu.matter.ReplicationRequestPacket
import ru.dbotthepony.mc.otm.network.MenuNetworkChannel
import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak
import java.util.function.Predicate
import kotlin.math.ceil
import kotlin.math.roundToInt

@MouseTweaksDisableWheelTweak
class MatterPanelScreen(
	menu: MatterPanelMenu,
	inventory: Inventory,
	title: Component
) : MatteryScreen<MatterPanelMenu>(menu, inventory, title) {
	override fun makeMainFrame(): FramePanel<MatteryScreen<*>> {
		var isPatternView = true
		var scrollPatterns = 0
		var scrollTasks = 0

		val frame = FramePanel.padded(this, null, GRID_WIDTH * AbstractSlotPanel.SIZE + ScrollBarConstants.WIDTH + 4f, GRID_HEIGHT * AbstractSlotPanel.SIZE + 2f, title)
		frame.dockPadding = frame.dockPadding.copy(top = frame.dockPadding.top + 2f)

		frame.makeCloseButton()
		frame.onClose { onClose() }

		val controls = DeviceControls(this, frame)

		LargeBooleanRectangleButtonPanel(
			this,
			controls,
			prop = menu.isAscendingGS,
			skinElementActive = Widgets18.ARROW_UP,
			skinElementInactive = Widgets18.ARROW_DOWN,
			tooltipActive = TranslatableComponent("otm.gui.sorting.ascending"),
			tooltipInactive = TranslatableComponent("otm.gui.sorting.descending"),
		).also {
			controls.addButton(it)
		}

		LargeEnumRectangleButtonPanel(this, controls, enum = ItemSorter::class.java, prop = menu.sortingGS, defaultValue = ItemSorter.DEFAULT).also {
			controls.addButton(it)
			it.add(ItemSorter.DEFAULT, skinElement = Widgets18.SORT_DEFAULT, tooltip = ItemSorter.DEFAULT.title)
			it.add(ItemSorter.NAME, skinElement = Widgets18.SORT_ALPHABET, tooltip = ItemSorter.NAME.title)
			it.add(ItemSorter.ID, skinElement = Widgets18.SORT_ID, tooltip = ItemSorter.ID.title)
			it.add(ItemSorter.MOD, skinElement = Widgets18.SORT_MODID, tooltip = ItemSorter.MOD.title)
			it.add(ItemSorter.MATTER_VALUE, skinElement = Widgets18.SORT_MATTER_VALUE, tooltip = ItemSorter.MATTER_VALUE.title)
			it.add(ItemSorter.MATTER_COMPLEXITY, skinElement = Widgets18.SORT_MATTER_COMPLEXITY, tooltip = ItemSorter.MATTER_COMPLEXITY.title)
			it.finish()
		}

		val scrollBar = DiscreteScrollBarPanel(this, frame, {
			if (isPatternView) {
				integerDivisionDown(menu.patternsFiltered.size, GRID_WIDTH)
			} else {
				integerDivisionDown(menu.tasksFiltered.size, GRID_WIDTH)
			}
		}, { _, _, new ->
			if (isPatternView)
				scrollPatterns = new
			else
				scrollTasks = new
		})

		object : TextInputPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) {
			init {
				width = frame.width * 0.4f
				x = frame.width - width - FramePanel.PADDING - 6f
				y = 4f
				placeholder = TranslatableComponent("otm.gui.quicksearch")
			}

			override fun onTextChanged(old: String, new: String) {
				if (new == "") {
					menu.filter = Predicate { true }
				} else {
					val new = new.lowercase()
					menu.filter = Predicate { it.description.string.lowercase().contains(new) }
					scrollBar.scroll = scrollBar.scroll
				}
			}
		}

		scrollBar.dock = Dock.RIGHT

		frame.Tab(onOpen = { isPatternView = true; scrollBar.scroll = scrollPatterns }, activeIcon = PATTERN_LIST_ACTIVE, inactiveIcon = PATTERN_LIST_INACTIVE).also {
			it.tooltips.add(TranslatableComponent("otm.gui.matter_panel.patterns"))
		}

		frame.Tab(onOpen = { isPatternView = false; scrollBar.scroll = scrollTasks }, activeIcon = TASK_LIST_ACTIVE, inactiveIcon = TASK_LIST_INACTIVE).also {
			it.tooltips.add(TranslatableComponent("otm.gui.matter_panel.tasks"))
		}

		val canvas = object : EditablePanel<MatterPanelScreen>(this@MatterPanelScreen, frame, width = GRID_WIDTH * AbstractSlotPanel.SIZE) {
			init {
				dock = Dock.LEFT
			}

			override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
				return scrollBar.mouseScrolledInner(x, y, scroll)
			}
		}

		for (row in 0 until GRID_HEIGHT) {
			val rowCanvas = object : EditablePanel<MatterPanelScreen>(this@MatterPanelScreen, canvas, height = AbstractSlotPanel.SIZE) {
				init {
					dock = Dock.TOP
				}

				override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
					return false
				}
			}

			for (i in 0 until GRID_WIDTH) {
				object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, rowCanvas) {
					init {
						dock = Dock.LEFT
					}

					private val index: Int get() = (scrollBar.scroll + row) * GRID_WIDTH + i

					override val itemStack: ItemStack get() {
						if (isPatternView) {
							return menu.patternsFiltered.getOrNull(index)?.stack() ?: ItemStack.EMPTY
						} else {
							return menu.tasksFiltered.getOrNull(index)?.let { it.stack(it.required + it.inProgress) } ?: ItemStack.EMPTY
						}
					}

					override fun getItemStackTooltip(stack: ItemStack): List<Component> {
						val list = super.getItemStackTooltip(stack).toMutableList()

						if (isPatternView) {
							menu.patternsFiltered.getOrNull(index)?.let {
								list.add(TranslatableComponent(
									"otm.item.pattern.research",
									String.format("%.2f", it.researchPercent * 100.0)
								).withStyle(ChatFormatting.AQUA))

								if (ru.dbotthepony.mc.otm.client.minecraft.options.advancedItemTooltips) {
									val researchAdvance = MatterManager.getResearchAdvance(it.item)
									val required = ceil(1.0 / researchAdvance).toInt()
									val researched = (required.toDouble() * it.researchPercent).toInt()

									list.add(TranslatableComponent(
										"otm.item.pattern.research.item_count",
										researched,
										required
									).withStyle(ChatFormatting.DARK_GRAY))
									list.add(TranslatableComponent(
										"otm.item.pattern.research.advance",
										String.format("%.2f", researchAdvance * 100.0)
									).withStyle(ChatFormatting.DARK_GRAY))
								}
							}
						} else {
							menu.tasksFiltered.getOrNull(index)?.let {
								list.add(TranslatableComponent("otm.gui.matter_task.total", it.total).withStyle(ChatFormatting.GRAY))
								list.add(TranslatableComponent("otm.gui.matter_task.required", it.required).withStyle(ChatFormatting.GRAY))
								list.add(TranslatableComponent("otm.gui.matter_task.in_progress", it.inProgress).withStyle(ChatFormatting.GRAY))
								list.add(TranslatableComponent("otm.gui.matter_task.finished", it.finished).withStyle(ChatFormatting.GRAY))
							}
						}

						return list
					}

					override fun renderRegular(graphics: GuiGraphics, itemstack: ItemStack, countOverride: String?) {
						if (isPatternView) {
							super.renderRegular(graphics, itemstack, "")

							menu.patternsFiltered.getOrNull(index)?.let {
								if (it.researchPercent < 1f) {
									font.drawScaledAligned(graphics.pose(), graphics.bufferSource(), (it.researchPercent * 100.0).toInt().toString() + "%", .5f, RenderGravity.BOTTOM_RIGHT, width - 1f, height - 1f, true, color = RGBAColor.WHITE)
								}
							}
						} else {
							super.renderRegular(graphics, itemstack, countOverride)

							menu.tasksFiltered.getOrNull(index)?.let {
								val progress = it.finished.toFloat() / it.total.toFloat()
								val processing = it.inProgress.toFloat() / it.total.toFloat()

								val barLeft = 2f
								val barTop = 2f
								val barWidth = width - 4f
								val barHeight = 1f

								graphics.renderRect(barLeft - .5f, barTop - .5f, barWidth + 1f, barHeight + 1f, color = RGBAColor.WHITE)
								graphics.renderRect(barLeft, barTop, barWidth, barHeight, color = RGBAColor.BLACK)
								graphics.renderRect(barLeft, barTop, barWidth * progress, barHeight, color = RGBAColor.GREEN)
								graphics.renderRect(barLeft + barWidth * progress, barTop, barWidth * processing, barHeight, color = RGBAColor.YELLOW)
							}
						}
					}

					override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean {
						if (isPatternView) {
							if (minecraft?.player?.isSpectator != true)
								menu.patternsFiltered.getOrNull(index)?.let(this@MatterPanelScreen::openPattern)?.let {
									frame.blockingWindow = it
								}
						} else {
							menu.tasksFiltered.getOrNull(index)?.let(this@MatterPanelScreen::openTask)?.let {
								frame.blockingWindow = it
							}
						}

						return true
					}

					override fun mouseScrolledInner(x: Double, y: Double, scroll: Double): Boolean {
						return false
					}
				}
			}
		}

		return frame
	}

	private fun openTask(task: IReplicationTask<*>): FramePanel<MatterPanelScreen> {
		val frame = FramePanel.padded(this, null, 170f, 20f, TranslatableComponent("otm.gui.matter_panel.task"))

		frame.behaveAsWindow()

		object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, frame) {
			init {
				dock = Dock.LEFT
			}

			override val itemStack: ItemStack get() {
				return menu.tasksFiltered.firstOrNull { it.id == task.id }?.let { it.stack((it.required + it.inProgress).coerceAtLeast(1)) } ?: task.stack((task.required + task.inProgress).coerceAtLeast(1))
			}

			override fun tickInner() {
				super.tickInner()

				if (!menu.tasksFiltered.any { it.id == task.id }) {
					frame.remove()
				}
			}
		}

		ButtonPanel(this@MatterPanelScreen, frame, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.close"), onPress = frame::remove).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		if (minecraft?.player?.isSpectator != true) {
			ButtonPanel(this@MatterPanelScreen, frame, width = 80f, label = TranslatableComponent("otm.gui.matter_panel.cancel_task"), onPress = Runnable {
				menu.requestTaskCancel(task.id)
				frame.remove()
			}).also {
				it.dock = Dock.RIGHT
				it.dockLeft = 2f
			}
		}

		addPanel(frame)
		popup(frame)

		frame.toScreenCenter()

		return frame
	}

	private fun openPattern(pattern: IPatternState): FramePanel<MatterPanelScreen> {
		val frame = FramePanel.padded(this, null, 213f, (ButtonPanel.HEIGHT + 3f) * 4f + 38f, TranslatableComponent("otm.gui.matter_panel.task"))

		frame.behaveAsWindow()

		val rowTop = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
		val rowInput = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
		val rowBottom = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)
		val rowControls = EditablePanel(this, frame, height = ButtonPanel.HEIGHT)

		rowTop.dock = Dock.TOP
		rowTop.dockTop = 3f
		rowInput.dock = Dock.TOP
		rowInput.dockTop = 3f
		rowBottom.dock = Dock.TOP
		rowBottom.dockTop = 3f
		rowControls.dock = Dock.TOP
		rowControls.dockTop = 3f

		object : AbstractSlotPanel<MatterPanelScreen>(this@MatterPanelScreen, rowInput) {
			init {
				dock = Dock.LEFT
				dockRight = 2f
			}

			override val itemStack: ItemStack get() {
				return pattern.stack()
			}

			override fun getItemStackTooltip(stack: ItemStack): List<Component> {
				return super.getItemStackTooltip(stack).toMutableList().also {
					it.add(TranslatableComponent(
						"otm.item.pattern.research",
						String.format("%.2f", pattern.researchPercent * 100.0)
					).withStyle(ChatFormatting.AQUA))
				}
			}

			override fun tickInner() {
				super.tickInner()

				if (!menu.patternsFiltered.any { it.id == pattern.id }) {
					frame.remove()
				}
			}
		}

		var count = 1

		val input = object : TextInputPanel<MatterPanelScreen>(this@MatterPanelScreen, rowInput) {
			init {
				dock = Dock.FILL
				text = "1"
				requestFocus()
			}

			fun increase(amount: Int) {
				count = 1

				try {
					count = text.toInt().coerceIn(1, 99999)
				} catch (_: NumberFormatException) {
				}

				if (count == 1 && amount > 0)
					text = amount.toString()
				else
					text = (count + amount).coerceIn(1, 99999).toString()
			}

			override fun tickInner() {
				super.tickInner()

				try {
					count = text.toInt().coerceIn(1, 99999)
				} catch (_: NumberFormatException) {
				}
			}

			fun send() {
				count = 1

				try {
					count = text.toInt().coerceIn(1, 99999)
				} catch (_: NumberFormatException) {
				}

				MenuNetworkChannel.sendToServer(ReplicationRequestPacket(pattern.id, count))
				frame.remove()
			}
		}

		ButtonPanel(this, rowTop, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.increase_by", 8), onPress = Runnable { input.increase(8) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowTop, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.increase_by", 64), onPress = Runnable { input.increase(64) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowTop, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.increase_by", 256), onPress = Runnable { input.increase(256) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowBottom, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.decrease_by", 8), onPress = Runnable { input.increase(-8) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowBottom, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.decrease_by", 64), onPress = Runnable { input.increase(-64) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowBottom, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.decrease_by", 256), onPress = Runnable { input.increase(-256) }).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowControls, width = 40f, label = TranslatableComponent("otm.gui.matter_panel.cancel"), onPress = frame::remove).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		ButtonPanel(this, rowControls, width = 82f, label = TranslatableComponent("otm.gui.matter_panel.send"), onPress = input::send).also {
			it.dock = Dock.RIGHT
			it.dockLeft = 2f
		}

		addPanel(frame)
		popup(frame)

		object : Label<MatterPanelScreen>(this@MatterPanelScreen, frame, text = TextComponent("")) {
			init {
				dock = Dock.TOP
				dockTop = 5f
			}

			override fun tickInner() {
				super.tickInner()

				text = TranslatableComponent(
					"otm.gui.matter_panel.matter_stored",
					menu.totalMatterStored.formatMatter(formatAsReadable = ShiftPressedCond),
				)
			}
		}

		object : Label<MatterPanelScreen>(this@MatterPanelScreen, frame, text = TextComponent("")) {
			init {
				dock = Dock.TOP
				dockTop = 3f
			}

			override fun tickInner() {
				super.tickInner()

				text = TranslatableComponent(
					"otm.gui.matter_panel.matter_required",
					(MatterManager.get(pattern.item).matter * count).formatMatter(formatAsReadable = ShiftPressedCond),
				)
			}
		}

		object : Label<MatterPanelScreen>(this@MatterPanelScreen, frame, text = TextComponent("")) {
			init {
				dock = Dock.TOP
				dockTop = 3f
			}

			override fun tickInner() {
				super.tickInner()

				text = TranslatableComponent(
					"otm.gui.matter_panel.complexity",
					formatTickDuration((MatterManager.get(pattern.item).complexity * count).roundToInt(), true),
				)
			}
		}

		frame.toScreenCenter()

		return frame
	}

	companion object {
		const val GRID_HEIGHT = 8
		const val GRID_WIDTH = 9

		val PATTERN_LIST_ACTIVE = WidgetLocation.PATTERN_PANEL_TABS.sprite(0f, 0f, 14f, 23f)
		val TASK_LIST_ACTIVE = WidgetLocation.PATTERN_PANEL_TABS.sprite(28f, 0f, 16f, 16f)
		val PATTERN_LIST_INACTIVE = WidgetLocation.PATTERN_PANEL_TABS.sprite(14f, 0f, 14f, 23f)
		val TASK_LIST_INACTIVE = WidgetLocation.PATTERN_PANEL_TABS.sprite(44f, 0f, 16f, 16f)
	}
}
