JewelBank = JewelBank or {}
JewelBank.Opened = JewelBank.Opened or false
JewelBank.Selected = JewelBank.Selected or 1
JewelBank.MouseDown = JewelBank.MouseDown or false
JewelBank.RightMouseDown = JewelBank.RightMouseDown or false
JewelBank.Balances = JewelBank.Balances or {}
JewelBank.LastJDown = JewelBank.LastJDown or false
JewelBank.LastEscapeDown = JewelBank.LastEscapeDown or false
JewelBank.SelectedAmount = JewelBank.SelectedAmount or tonumber((JewelBankConfig or {}).defaultAmount or 1) or 1
JewelBank.AmountInput = JewelBank.AmountInput or tostring(JewelBank.SelectedAmount or 1)
JewelBank.AmountFocused = JewelBank.AmountFocused or false
JewelBank.KeyState = JewelBank.KeyState or {}
JewelBank.Scroll = JewelBank.Scroll or 0
JewelBank.Message = JewelBank.Message or ""
JewelBank.MessageKind = JewelBank.MessageKind or "info"
JewelBank.MessageFrames = JewelBank.MessageFrames or 0
JewelBank.Dragging = JewelBank.Dragging or false
JewelBank.DragOffsetX = JewelBank.DragOffsetX or 0
JewelBank.DragOffsetY = JewelBank.DragOffsetY or 0

local config = JewelBankConfig or {}
local packets = config
local text = config.text or {}
JewelBank.Jewels = config.jewels or {}

local digitKeys = {
	{ key = 48, value = "0" },
	{ key = 49, value = "1" },
	{ key = 50, value = "2" },
	{ key = 51, value = "3" },
	{ key = 52, value = "4" },
	{ key = 53, value = "5" },
	{ key = 54, value = "6" },
	{ key = 55, value = "7" },
	{ key = 56, value = "8" },
	{ key = 57, value = "9" },
	{ key = 96, value = "0" },
	{ key = 97, value = "1" },
	{ key = 98, value = "2" },
	{ key = 99, value = "3" },
	{ key = 100, value = "4" },
	{ key = 101, value = "5" },
	{ key = 102, value = "6" },
	{ key = 103, value = "7" },
	{ key = 104, value = "8" },
	{ key = 105, value = "9" },
}

local function jewelBankBox(x, y, width, height, red, green, blue, alpha)
	if ClientAPI.RenderColorBox ~= nil then
		return ClientAPI.RenderColorBox(x, y, width, height, red, green, blue, alpha)
	end

	return ClientAPI.RenderBox(x, y, width, height, (alpha or 180) / 255, 1)
end

local function jewelBankParseBalances(data)
	for pair in tostring(data or ""):gmatch("([^;]+)") do
		local key, value = pair:match("([^=]+)=([^=]+)")
		key = tonumber(key)
		value = tonumber(value)

		if key ~= nil and value ~= nil then
			JewelBank.Balances[key] = value
		end
	end
end

local function jewelBankSend(packetName, data)
	if ClientAPI ~= nil and ClientAPI.Send ~= nil then
		return ClientAPI.Send(packetName, data or "")
	end

	return false
end

local function jewelBankParseValue(data, key)
	return tostring(data or ""):match(key .. "=([^;]+)")
end

local function jewelBankSetMessage(kind, message)
	JewelBank.MessageKind = kind or "info"
	JewelBank.Message = message or ""
	JewelBank.MessageFrames = 180
end

function JewelBank.Open(data)
	jewelBankParseBalances(data)
	JewelBank.Opened = true
	JewelBank.Selected = JewelBank.Selected or 1
	JewelBank.Scroll = JewelBank.Scroll or 0
	JewelBank.SelectedAmount = JewelBank.SelectedAmount or tonumber(config.defaultAmount or 1) or 1
	JewelBank.AmountInput = JewelBank.AmountInput or tostring(JewelBank.SelectedAmount or 1)
	JewelBank.MouseDown = false
	JewelBank.Dragging = false
	JewelBank.LastEscapeDown = false
	jewelBankSend(packets.packetReady or "JewelBankClientReady", "opened=true")
end

function JewelBank.Close()
	if JewelBank.Opened == true then
		JewelBank.Opened = false
		JewelBank.MouseDown = false
		JewelBank.Dragging = false
		JewelBank.LastEscapeDown = false
		jewelBankSend(packets.packetClose or "JewelBankClose", "closed=true")
	end
end

local function jewelBankGetRect()
	local window = config.window or {}
	local width = tonumber(window.width or 260) or 260
	local height = tonumber(window.height or 260) or 260
	local x = tonumber(JewelBank.PositionX)
	local y = tonumber(JewelBank.PositionY)

	if x == nil then
		x = tonumber(window.x or 213) or 213
	end

	if y == nil then
		y = tonumber(window.y or 100) or 100
	end

	if JewelBank.PositionX == nil and window.center == true then
		local screenWidth = ClientAPI.GetInterfaceWidth ~= nil and tonumber(ClientAPI.GetInterfaceWidth() or 0) or 0

		if screenWidth > 0 then
			x = math.floor((screenWidth - width) / 2)
			y = tonumber(window.y or 100) or 100
		end
	end

	return x, y, width, height
end

local function jewelBankGetInterfaceSize()
	local screenWidth = 0
	local screenHeight = 0

	if ClientAPI.GetInterfaceWidth ~= nil then
		screenWidth = tonumber(ClientAPI.GetInterfaceWidth() or 0) or 0
	end

	if ClientAPI.GetInterfaceHeight ~= nil then
		screenHeight = tonumber(ClientAPI.GetInterfaceHeight() or 0) or 0
	end

	if screenWidth > 0 and screenHeight > 0 then
		return screenWidth, screenHeight
	end

	return 640, 480
end

local function jewelBankClampPosition(x, y)
	local window = config.window or {}
	local width = tonumber(window.width or 260) or 260
	local height = tonumber(window.height or 260) or 260
	local screenWidth = tonumber(window.dragScreenWidth or 0) or 0
	local screenHeight = tonumber(window.dragScreenHeight or 0) or 0

	if screenWidth <= 0 or screenHeight <= 0 then
		local interfaceWidth, interfaceHeight = jewelBankGetInterfaceSize()

		if screenWidth <= 0 then
			screenWidth = interfaceWidth
		end

		if screenHeight <= 0 then
			screenHeight = interfaceHeight
		end
	end

	if screenWidth <= 0 then
		screenWidth = 640
	end

	if screenHeight <= 0 then
		screenHeight = 480
	end

	if x < 0 then
		x = 0
	elseif x > screenWidth - width then
		x = screenWidth - width
	end

	if y < 0 then
		y = 0
	elseif y > screenHeight - height then
		y = screenHeight - height
	end

	return math.floor(x), math.floor(y)
end

local function jewelBankUpdateDrag()
	local x, y, width, _ = jewelBankGetRect()
	local mouseX = ClientAPI.GetMouseX()
	local mouseY = ClientAPI.GetMouseY()
	local leftPush = ClientAPI.IsMouseLeftButtonPush ~= nil and ClientAPI.IsMouseLeftButtonPush() == true
	local leftPop = ClientAPI.IsMouseLeftButtonPop ~= nil and ClientAPI.IsMouseLeftButtonPop() == true

	if leftPop == true then
		JewelBank.Dragging = false
		return false
	end

	if JewelBank.Dragging == true then
		local newX, newY = jewelBankClampPosition(mouseX - JewelBank.DragOffsetX, mouseY - JewelBank.DragOffsetY)
		JewelBank.PositionX = newX
		JewelBank.PositionY = newY
		ClientAPI.BlockMouse()
		return true
	end

	local titleBarHeight = tonumber((config.window or {}).titleBarHeight or 28) or 28
	local onCloseButton = ClientAPI.IsMouseInRect(x + width - 22, y + 3, 19, 19)
	if leftPush == true and onCloseButton ~= true and ClientAPI.IsMouseInRect(x + 8, y + 3, width - 36, titleBarHeight) then
		JewelBank.Dragging = true
		JewelBank.DragOffsetX = mouseX - x
		JewelBank.DragOffsetY = mouseY - y
		ClientAPI.BlockMouse()
		return true
	end

	return false
end

local function jewelBankGetRowAt(mouseX, mouseY)
	local x, y, _, _ = jewelBankGetRect()
	local listX = x + 18
	local listY = y + 58
	local rowHeight = 20
	local maxVisible = 8
	local scroll = tonumber(JewelBank.Scroll or 0) or 0

	for visibleIndex = 1, maxVisible do
		local index = visibleIndex + scroll
		local jewel = JewelBank.Jewels[index]
		local rowY = listY + ((visibleIndex - 1) * rowHeight)
		if jewel == nil then
			break
		end
		if mouseX >= listX and mouseX <= x + 236 and mouseY >= rowY and mouseY <= rowY + 18 then
			return jewel
		end
	end

	return nil
end

local function jewelBankClampScroll()
	local maxVisible = 8
	local maxScroll = #JewelBank.Jewels - maxVisible
	if maxScroll < 0 then
		maxScroll = 0
	end

	local scroll = tonumber(JewelBank.Scroll or 0) or 0
	if scroll < 0 then
		scroll = 0
	elseif scroll > maxScroll then
		scroll = maxScroll
	end

	JewelBank.Scroll = scroll
	return scroll, maxScroll
end

local function jewelBankScroll(delta)
	if delta == nil or delta == 0 then
		return
	end

	JewelBank.Scroll = (tonumber(JewelBank.Scroll or 0) or 0) + delta
	jewelBankClampScroll()
end

local function jewelBankGetItemIndex(item)
	if item == nil then
		return nil
	end

	if item.model ~= nil then
		return tonumber(item.model)
	end

	local section = tonumber(item.section)
	local index = tonumber(item.index)
	if section == nil or index == nil then
		return nil
	end

	return (section * 512) + index
end

local function jewelBankRenderItemIcon(jewel, x, y)
	if jewel == nil or jewel.item == nil or ClientAPI.RenderItem == nil then
		return false
	end

	local item = jewel.item
	local itemIndex = jewelBankGetItemIndex(item)
	if itemIndex == nil then
		return false
	end

	return ClientAPI.RenderItem(
		x + (tonumber(item.x or 0) or 0),
		y + (tonumber(item.y or 0) or 0),
		tonumber(item.width or 13) or 13,
		tonumber(item.height or 13) or 13,
		itemIndex,
		tonumber(item.level or 0) or 0,
		tonumber(item.option1 or 0) or 0,
		tonumber(item.extOption or 0) or 0
	)
end

local function jewelBankNormalizeAmount()
	local amount = tonumber(JewelBank.AmountInput or "")
	local maxAmount = tonumber(config.maxAmount or 999) or 999

	if amount == nil or amount < 1 then
		amount = 1
	end

	if amount > maxAmount then
		amount = maxAmount
	end

	JewelBank.SelectedAmount = amount
	JewelBank.AmountInput = tostring(amount)
end

local function jewelBankDepositSelected()
	if JewelBank.Selected ~= nil then
		jewelBankNormalizeAmount()
		jewelBankSend(packets.packetDeposit or "JewelBankDeposit", "id=" .. tostring(JewelBank.Selected) .. " amount=" .. tostring(JewelBank.SelectedAmount or 1))
	end
end

local function jewelBankDepositMouseSlot()
	if ClientAPI.GetInventoryMouseSlot == nil then
		return false
	end

	local slot = tonumber(ClientAPI.GetInventoryMouseSlot() or -1) or -1
	if slot < 0 then
		return false
	end

	jewelBankSend(packets.packetDepositSlot or "JewelBankDepositSlot", "slot=" .. tostring(slot))
	return true
end

local function jewelBankUpdateAmountInput()
	if JewelBank.AmountFocused ~= true or ClientAPI.IsKeyDown == nil then
		return
	end

	for _, entry in ipairs(digitKeys) do
		local down = ClientAPI.IsKeyDown(entry.key)
		if down == true and JewelBank.KeyState[entry.key] ~= true then
			local current = tostring(JewelBank.AmountInput or "")
			current = current .. entry.value
			current = current:gsub("^0+", "")
			if current == "" then
				current = "0"
			end

			if #current > 3 then
				current = current:sub(1, 3)
			end

			JewelBank.AmountInput = current
			jewelBankNormalizeAmount()
		end
		JewelBank.KeyState[entry.key] = down == true
	end

	local backspaceDown = ClientAPI.IsKeyDown(8)
	if backspaceDown == true and JewelBank.KeyState[8] ~= true then
		local current = tostring(JewelBank.AmountInput or "")
		current = current:sub(1, #current - 1)
		if current == "" then
			current = "1"
		end
		JewelBank.AmountInput = current
		jewelBankNormalizeAmount()
	end
	JewelBank.KeyState[8] = backspaceDown == true
end

local function jewelBankClick()
	local x, y, width, _ = jewelBankGetRect()
	local mouseX = ClientAPI.GetMouseX()
	local mouseY = ClientAPI.GetMouseY()

	if ClientAPI.IsMouseInRect(x + width - 22, y + 3, 19, 19) then
		JewelBank.Close()
		return
	end

	local _, maxScroll = jewelBankClampScroll()
	if maxScroll > 0 then
		if ClientAPI.IsMouseInRect(x + 234, y + 58, 8, 12) then
			jewelBankScroll(-1)
			return
		end

		if ClientAPI.IsMouseInRect(x + 234, y + 206, 8, 12) then
			jewelBankScroll(1)
			return
		end
	end

	local rowJewel = jewelBankGetRowAt(mouseX, mouseY)
	if rowJewel ~= nil then
		JewelBank.Selected = rowJewel.id
		JewelBank.AmountFocused = false
		jewelBankSend(packets.packetSelect or "JewelBankSelect", "id=" .. tostring(rowJewel.id) .. " name=" .. rowJewel.name)
		return
	end

	if ClientAPI.IsMouseInRect(x + 106, y + 228, 120, 18) then
		JewelBank.AmountFocused = true
		JewelBank.AmountInput = ""
		return
	end

	JewelBank.AmountFocused = false

	if ClientAPI.IsMouseInRect(x + 24, y + 258, 92, 20) and JewelBank.Selected ~= nil then
		jewelBankDepositSelected()
		return
	end

	if ClientAPI.IsMouseInRect(x + 144, y + 258, 92, 20) and JewelBank.Selected ~= nil then
		jewelBankNormalizeAmount()
		jewelBankSend(packets.packetWithdraw or "JewelBankWithdraw", "id=" .. tostring(JewelBank.Selected) .. " amount=" .. tostring(JewelBank.SelectedAmount or 1))
	end
end

local function JewelBank_UpdateMouse()
	if ClientAPI.IsWindowActive ~= nil and ClientAPI.IsWindowActive() ~= true then
		JewelBank.LastJDown = false
		JewelBank.MouseDown = false
		JewelBank.RightMouseDown = false
		return false
	end

	local openKey = tonumber(config.key or 74) or 74
	local jDown = ClientAPI.IsKeyDown ~= nil and ClientAPI.IsKeyDown(openKey)
	if jDown == true and JewelBank.LastJDown ~= true then
		if JewelBank.Opened == true then
			JewelBank.Close()
		else
			jewelBankSend(packets.packetOpenRequest or "JewelBankOpenRequest", "key=J")
		end
	end

	JewelBank.LastJDown = jDown == true

	if JewelBank.Opened ~= true then
		return false
	end

	if jewelBankUpdateDrag() == true then
		return true
	end

	local rightDown = ClientAPI.IsMouseRightButton ~= nil and ClientAPI.IsMouseRightButton()
	local shiftDown = ClientAPI.IsKeyDown ~= nil and ClientAPI.IsKeyDown(tonumber(config.shiftKey or 16) or 16)
	if shiftDown == true and rightDown == true and JewelBank.RightMouseDown == false then
		JewelBank.RightMouseDown = true
		if jewelBankDepositMouseSlot() == true then
			ClientAPI.BlockMouse()
			return true
		end
	elseif rightDown == false then
		JewelBank.RightMouseDown = false
	end

	local x, y, width, height = jewelBankGetRect()
	if ClientAPI.IsMouseInRect(x, y, width, height) then
		if ClientAPI.GetMouseWheel ~= nil then
			local wheel = tonumber(ClientAPI.GetMouseWheel() or 0) or 0
			if wheel > 0 then
				jewelBankScroll(-1)
			elseif wheel < 0 then
				jewelBankScroll(1)
			end
		end

		local leftDown = ClientAPI.IsMouseLeftButtonPush() or ClientAPI.IsMouseLeftButton()

		if leftDown and JewelBank.MouseDown == false then
			JewelBank.MouseDown = true
			jewelBankClick()
		elseif leftDown == false then
			JewelBank.MouseDown = false
		end

		ClientAPI.BlockMouse()
		return true
	end

	return false
end

local function JewelBank_UpdateKey(key, down, wasDown)
	if JewelBank.Opened ~= true or key ~= 27 or down ~= true then
		return false
	end

	if ClientAPI.ConsumeKey ~= nil then
		ClientAPI.ConsumeKey(27)
	end

	if wasDown ~= true then
		JewelBank.Close()
	end

	return true
end

local function JewelBank_Render()
	if JewelBank.Opened ~= true then
		if JewelBank.MessageFrames ~= nil and JewelBank.MessageFrames > 0 then
			JewelBank.MessageFrames = JewelBank.MessageFrames - 1
		end
		return
	end

	jewelBankUpdateAmountInput()

	local x, y, width, height = jewelBankGetRect()

	if RenderImage2 ~= nil then
		RenderImage2("Interface\\HUD\\top_back_3.tga", x, y, width, height, 0.0, 0.0, 854.0, 917.0)
	else
		jewelBankBox(x, y, width, height, 34, 37, 44, 230)
		jewelBankBox(x + 5, y + 28, width - 10, height - 34, 4, 4, 5, 235)
	end

	ClientAPI.RenderText(x, y + 9, text.title or "Jewel Bank", width, 10, 3, 255, 255, 255, 255)

	local listX = x + 18
	local listY = y + 58
	local rowHeight = 20
	local maxVisible = 8
	local scroll, maxScroll = jewelBankClampScroll()

	ClientAPI.RenderText(x + 34, y + 44, text.item or "Item", 150, 10, 1, 255, 225, 0, 255)
	ClientAPI.RenderText(x + 188, y + 44, text.amount or "Qtd", 40, 10, 4, 255, 225, 0, 255)

	for visibleIndex = 1, maxVisible do
		local index = visibleIndex + scroll
		local jewel = JewelBank.Jewels[index]
		if jewel == nil then
			break
		end

		local rowY = listY + ((visibleIndex - 1) * rowHeight)
		local rowR = 0
		local rowG = 0
		local rowB = 0
		local rowA = 0
		local textR = 220
		local textG = 220
		local textB = 220

		if JewelBank.Selected == jewel.id then
			rowR = 79
			rowG = 86
			rowB = 100
			rowA = 255
			textR = 255
			textG = 220
			textB = 120
		elseif ClientAPI.IsMouseInRect(listX, rowY, 218, 18) then
			rowR = 52
			rowG = 57
			rowB = 67
			rowA = 230
		end

		if rowA > 0 then
			jewelBankBox(listX - 3, rowY, 218, 18, rowR, rowG, rowB, rowA)
		end
		jewelBankRenderItemIcon(jewel, x + 22, rowY + 4)
		ClientAPI.RenderText(x + 40, rowY + 4, jewel.name, 132, 10, 1, textR, textG, textB, 255)
		ClientAPI.RenderText(x + 188, rowY + 4, tostring(JewelBank.Balances[jewel.id] or 0), 40, 10, 4, 120, 255, 120, 255)
	end

	if maxScroll > 0 then
		local trackX = x + 235
		local trackY = y + 58
		local trackH = (maxVisible * rowHeight) - 2
		local thumbH = math.floor(trackH * (maxVisible / #JewelBank.Jewels))
		if thumbH < 22 then
			thumbH = 22
		end

		local thumbY = trackY
		if maxScroll > 0 then
			thumbY = trackY + math.floor((trackH - thumbH) * (scroll / maxScroll))
		end

		jewelBankBox(trackX, trackY, 4, trackH, 40, 44, 55, 220)
		jewelBankBox(trackX - 1, thumbY, 6, thumbH, 170, 170, 170, 230)
	end

	ClientAPI.RenderText(x + 22, y + 232, text.quantity or "Quantidade", 78, 10, 1, 255, 225, 0, 255)
	jewelBankBox(x + 106, y + 228, 120, 18, JewelBank.AmountFocused and 59 or 43, JewelBank.AmountFocused and 66 or 48, JewelBank.AmountFocused and 124 or 95, 255)
	local amountText = tostring(JewelBank.AmountInput or "")
	if amountText == "" then
		amountText = "_"
	end
	ClientAPI.RenderText(x + 106, y + 233, amountText, 120, 10, 3, 255, 255, 255, 255)

	local depositHover = ClientAPI.IsMouseInRect(x + 24, y + 258, 92, 20)
	jewelBankBox(x + 24, y + 258, 92, 20, depositHover and 59 or 43, depositHover and 66 or 48, depositHover and 124 or 95, 255)
	ClientAPI.RenderText(x + 24, y + 264, text.deposit or "Depositar", 92, 10, 3, 255, 255, 255, 255)

	local withdrawHover = ClientAPI.IsMouseInRect(x + 144, y + 258, 92, 20)
	jewelBankBox(x + 144, y + 258, 92, 20, withdrawHover and 59 or 43, withdrawHover and 66 or 48, withdrawHover and 124 or 95, 255)
	ClientAPI.RenderText(x + 144, y + 264, text.withdraw or "Retirar", 92, 10, 3, 255, 255, 255, 255)

	if JewelBank.MessageFrames ~= nil and JewelBank.MessageFrames > 0 then
		JewelBank.MessageFrames = JewelBank.MessageFrames - 1
	end
end

if ClientAPI ~= nil and ClientAPI.OnPacket ~= nil then
	ClientAPI.OnPacket(function(packetName, data)
		if packetName == (packets.packetOpen or "RunJewelBankOpen") then
			JewelBank.Open(data)
		elseif packetName == (packets.packetBalance or "JewelBankBalance") then
			jewelBankParseBalances(data)
		elseif packetName == (packets.packetMessage or "JewelBankMessage") then
			jewelBankSetMessage(jewelBankParseValue(data, "kind") or "info", jewelBankParseValue(data, "message") or tostring(data or ""))
		end
	end)
end

if ClientHooks ~= nil then
	ClientHooks.RegisterUpdateMouse("JewelBank", JewelBank_UpdateMouse)
	ClientHooks.RegisterUpdateKey("JewelBank", JewelBank_UpdateKey)
	if ClientHooks.RegisterRenderTop ~= nil then
		ClientHooks.RegisterRenderTop("JewelBank", JewelBank_Render)
	else
		ClientHooks.RegisterRender("JewelBank", JewelBank_Render)
	end
end
