--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]

local RECEIVER_NAME = "ixInventoryItem"

-- The queue for the rendered icons.
ICON_RENDER_QUEUE = ICON_RENDER_QUEUE or {}

local iconSize = SScaleMin(90 / 3)

local function PaintLockedSlot(slot, w, h)
	surface.SetDrawColor(35, 35, 35, 85)
	surface.DrawRect(1, 1, w - SScaleMin(2 / 3), h - SScaleMin(2 / 3))
	surface.SetDrawColor(80, 80, 80, 255)
	surface.DrawOutlinedRect(1, 1, w - SScaleMin(2 / 3), h - SScaleMin(2 / 3))

	surface.SetDrawColor(80, 80, 80, 110)
	surface.DrawLine( 1, 1, iconSize - SScaleMin(2 / 3), iconSize - SScaleMin(2 / 3) )
	surface.DrawLine( 1, iconSize - SScaleMin(2 / 3), iconSize - SScaleMin(2 / 3), 1 )
end

-- To make making inventory variant, This must be followed up.
local function RenderNewIcon(panel, itemTable)
	local model = itemTable:GetModel()

	-- re-render icons
	if ((itemTable.iconCam and !ICON_RENDER_QUEUE[string.utf8lower(model)]) or itemTable.forceRender) then
		local iconCam = itemTable.iconCam
		iconCam = {
			cam_pos = iconCam.pos,
			cam_ang = iconCam.ang,
			cam_fov = iconCam.fov,
		}
		ICON_RENDER_QUEUE[string.utf8lower(model)] = true

		panel.Icon:RebuildSpawnIconEx(
			iconCam
		)
	end
end

local function InventoryAction(action, itemID, invID, data)
	net.Start("ixInventoryAction")
		net.WriteString(action)
		net.WriteUInt(itemID, 32)
		net.WriteUInt(invID, 32)
		net.WriteTable(data or {})
	net.SendToServer()
end

local PANEL = {}

AccessorFunc(PANEL, "itemTable", "ItemTable")
AccessorFunc(PANEL, "inventoryID", "InventoryID")

function PANEL:Init()
	self:Droppable(RECEIVER_NAME)
end

function PANEL:OnMousePressed(code)
	if (code == MOUSE_LEFT and self:IsDraggable()) then
		self:MouseCapture(true)
		self:DragMousePress(code)

		self.clickX, self.clickY = input.GetCursorPos()

		if (input.IsKeyDown(KEY_LSHIFT)) then
			self:DoShiftMove()
		end
	elseif (code == MOUSE_RIGHT and self.DoRightClick) then
		self:DoRightClick()
	end
end

function PANEL:OnMouseReleased(code)
	-- move the item into the world if we're dropping on something that doesn't handle inventory item drops
	if (!dragndrop.m_ReceiverSlot or dragndrop.m_ReceiverSlot.Name != RECEIVER_NAME) then
		self:OnDrop(dragndrop.IsDragging())
	end

	self:DragMouseRelease(code)
	self:SetZPos(99)
	self:MouseCapture(false)
end

function PANEL:DoShiftMove()
	local itemTable = self.itemTable
	local inventory = self.inventoryID
	local storageInv = ix.gui.openedStorage

	if (itemTable and inventory and storageInv and IsValid(storageInv)) then
		net.Start("ixInventoryShiftMove")
			net.WriteFloat(itemTable.id)
			net.WriteFloat(storageInv:GetStorageID())
		net.SendToServer()

		local path = "willardnetworks/inventory/"
		local randomSounds = {"inv_move1.wav", "inv_move2.wav", "inv_move3.wav", "inv_move4.mp3", "inv_move5.mp3", "inv_move6.mp3"}
		surface.PlaySound(path .. table.Random(randomSounds))
	end
end

function PANEL:DoRightClick()
	local itemTable = self.itemTable
	local inventory = self.inventoryID

	if (itemTable and inventory) then
		itemTable.player = LocalPlayer()

		local menu = DermaMenu()
		local override = hook.Run("CreateItemInteractionMenu", self, menu, itemTable)

		if (override == true) then
			if (menu.Remove) then
				menu:Remove()
			end

			return
		end

		for k, v in SortedPairs(itemTable.functions) do
			if (k == "drop" or k == "combine" or k == "combined" or (v.OnCanRun and v.OnCanRun(itemTable) == false)) then
				continue
			end

			-- is Multi-Option Function
			if (v.isMulti) then
				local subMenu, subMenuOption = menu:AddSubMenu(L(v.name or k), function()
					itemTable.player = LocalPlayer()
						local send = true

						if (v.OnClick) then
							send = v.OnClick(itemTable)
						end

						if (v.sound) then
							surface.PlaySound(v.sound)
						end

						if (send != false) then
							InventoryAction(k, itemTable.id, inventory)
						end
					itemTable.player = nil
				end)
				subMenuOption:SetImage(v.icon or "icon16/brick.png")

				if (v.multiOptions) then
					local options = isfunction(v.multiOptions) and v.multiOptions(itemTable, LocalPlayer()) or v.multiOptions

					for _, sub in pairs(options) do
						subMenu:AddOption(L(sub.name or "subOption"), function()
							itemTable.player = LocalPlayer()
								local send = true

								if (sub.OnClick) then
									send = sub.OnClick(itemTable)
								end

								if (sub.sound) then
									surface.PlaySound(sub.sound)
								end

								if (send != false) then
									InventoryAction(k, itemTable.id, inventory, sub.data)
								end
							itemTable.player = nil
						end)
					end
				end
			else
				menu:AddOption(L(v.name or k), function()
					itemTable.player = LocalPlayer()
						local send = true

						if (v.OnClick) then
							send = v.OnClick(itemTable)
						end

						if (v.sound) then
							surface.PlaySound(v.sound)
						end

						if (send != false) then
							InventoryAction(k, itemTable.id, inventory)
						end
					itemTable.player = nil
				end):SetImage(v.icon or "icon16/brick.png")
			end
		end

		-- we want drop to show up as the last option
		local info = itemTable.functions.drop

		if (info and info.OnCanRun and info.OnCanRun(itemTable) != false) then
			menu:AddOption(L(info.name or "drop"), function()
				itemTable.player = LocalPlayer()
					local send = true

					if (info.OnClick) then
						send = info.OnClick(itemTable)
					end

					if (info.sound) then
						surface.PlaySound(info.sound)
					end

					if (send != false) then
						InventoryAction("drop", itemTable.id, inventory)
					end
				itemTable.player = nil
			end):SetImage(info.icon or "icon16/brick.png")
		end

		menu:Open()
		for _, v in pairs(menu:GetChildren()[1]:GetChildren()) do
			v:SetFont("MenuFontNoClamp")
		end

		itemTable.player = nil
	end
end

local XYchanges = {
	[1] = {x = 1, y = 6},
	[2] = {x = 1, y = 7},
	[3] = {x = 1, y = 8},
	[4] = {x = 1, y = 9},
	[5] = {x = 1, y = 10},
}

function PANEL:OnDrop(bDragging, inventoryPanel, inventory, gridX, gridY)
	local item = self.itemTable

	if (!item or !bDragging) then
		return
	end

	if (inventory and inventory.vars and inventory.vars.isEquipSlots) then
		if (gridX == 5 and gridY <= 5) then
			gridX, gridY = XYchanges[gridY].x, XYchanges[gridY].y
		end
	end

	if (!IsValid(inventoryPanel)) then
		local inventoryID = self.inventoryID

		if (inventoryID) then
			InventoryAction("drop", item.id, inventoryID, {})
		end
	elseif (inventoryPanel:IsAllEmpty(gridX, gridY, item.width, item.height, self)) then
		local oldX, oldY = self.gridX, self.gridY

		if (oldX != gridX or oldY != gridY or self.inventoryID != inventoryPanel.invID) then
			self:Move(gridX, gridY, inventoryPanel)
		end
	elseif (inventoryPanel.combineItem) then
		local combineItem = inventoryPanel.combineItem
		local inventoryID = combineItem.invID

		if (inventoryID) then
			local info = combineItem.functions.combine

			if (info and info.OnCanRun and info.OnCanRun(combineItem, {item.id}) != false) then
				combineItem.player = LocalPlayer()

				if (combineItem.functions.combine.sound) then
					surface.PlaySound(combineItem.functions.combine.sound)
				end

				InventoryAction("combine", combineItem.id, inventoryID, {item.id})
				combineItem.player = nil
			end

			local info2 = item.functions.combined

			if (info2 and info2.OnCanRun and info2.OnCanRun(item, {combineItem.id}) != false) then
				item.player = LocalPlayer()
				
				InventoryAction("combined", item.id, inventoryID, {combineItem.id})
				item.player = nil
			end
		end
	end
end

function PANEL:Move(newX, newY, givenInventory, bNoSend)
	local iconSize2 = givenInventory.iconSize
	local oldX, oldY = self.gridX, self.gridY
	local oldParent = self:GetParent()

	if (givenInventory:OnTransfer(oldX, oldY, newX, newY, oldParent, bNoSend) == false) then
		return
	end

	local isEqSlots = givenInventory:IsEquipSlotsPanel()
	local x = (newX - 1 + (isEqSlots and newY > 5 and 4 or 0)) * iconSize2
	local y = (newY - 1 - (isEqSlots and newY > 5 and 5 or 0)) * iconSize2 + givenInventory:GetPadding(2)

	self.gridX = newX
	self.gridY = newY

	self:SetParent(givenInventory)
	self:SetPos(x, y)

	if (self.slots) then
		for _, v in ipairs(self.slots) do
			if (IsValid(v) and v.item == self) then
				v.item = nil
			end
		end
	end

	self.slots = {}

	for currentX = 1, self.gridW do
		for currentY = 1, self.gridH do
			local slot = givenInventory.slots[self.gridX + currentX - 1][self.gridY + currentY - 1]

			slot.item = self
			self.slots[#self.slots + 1] = slot
		end
	end

	local path = "willardnetworks/inventory/"
	local randomSounds = {"inv_move1.wav", "inv_move2.wav", "inv_move3.wav", "inv_move4.mp3", "inv_move5.mp3", "inv_move6.mp3"}
	surface.PlaySound(path..table.Random(randomSounds))
end

function PANEL:PaintOver(width, height)
	local itemTable = self.itemTable

	if (itemTable and itemTable.PaintOver) then
		itemTable.PaintOver(self, itemTable, width, height)
	end
end

function PANEL:ExtraPaint(width, height)
end

function PANEL:Paint(width, height)
	surface.SetDrawColor(0, 0, 0, 85)
	surface.DrawRect(2, 2, width - 0, height - 4)

	self:ExtraPaint(width, height)
end

vgui.Register("ixItemIcon", PANEL, "SpawnIcon")

DEFINE_BASECLASS("DModelPanel")

PANEL = {}

AccessorFunc(PANEL, "itemTable", "ItemTable")
AccessorFunc(PANEL, "inventoryID", "InventoryID")

function PANEL:Init()
	self:Droppable(RECEIVER_NAME)
end

function PANEL:OnMousePressed(code)
	if (code == MOUSE_LEFT and self:IsDraggable()) then
		self:MouseCapture(true)
		self:DragMousePress(code)

		self.clickX, self.clickY = input.GetCursorPos()

		if (input.IsKeyDown(KEY_LSHIFT)) then
			self:DoShiftMove()
		end
	elseif (code == MOUSE_RIGHT and self.DoRightClick) then
		self:DoRightClick()
	end
end

function PANEL:OnMouseReleased(code)
	-- move the item into the world if we're dropping on something that doesn't handle inventory item drops
	if (!dragndrop.m_ReceiverSlot or dragndrop.m_ReceiverSlot.Name != RECEIVER_NAME) then
		self:OnDrop(dragndrop.IsDragging())
	end

	self:DragMouseRelease(code)
	self:SetZPos(99)
	self:MouseCapture(false)
end

function PANEL:DoShiftMove()
	local itemTable = self.itemTable
	local inventory = self.inventoryID
	local storageInv = ix.gui.openedStorage

	if (itemTable and inventory and storageInv and IsValid(storageInv)) then
		net.Start("ixInventoryShiftMove")
			net.WriteFloat(itemTable.id)
			net.WriteFloat(storageInv:GetStorageID())
		net.SendToServer()

		local path = "willardnetworks/inventory/"
		local randomSounds = {"inv_move1.wav", "inv_move2.wav", "inv_move3.wav", "inv_move4.mp3", "inv_move5.mp3", "inv_move6.mp3"}
		surface.PlaySound(path .. table.Random(randomSounds))
	end
end

function PANEL:DoRightClick()
	local itemTable = self.itemTable
	local inventory = self.inventoryID

	if (itemTable and inventory) then
		itemTable.player = LocalPlayer()

		local menu = DermaMenu()
		local override = hook.Run("CreateItemInteractionMenu", self, menu, itemTable)

		if (override == true) then
			if (menu.Remove) then
				menu:Remove()
			end

			return
		end

		for k, v in SortedPairs(itemTable.functions) do
			if (k == "drop" or k == "combine" or (v.OnCanRun and v.OnCanRun(itemTable) == false)) then
				continue
			end

			-- is Multi-Option Function
			if (v.isMulti) then
				local subMenu, subMenuOption = menu:AddSubMenu(L(v.name or k), function()
					itemTable.player = LocalPlayer()
						local send = true

						if (v.OnClick) then
							send = v.OnClick(itemTable)
						end

						if (v.sound) then
							surface.PlaySound(v.sound)
						end

						if (send != false) then
							InventoryAction(k, itemTable.id, inventory)
						end
					itemTable.player = nil
				end)
				subMenuOption:SetImage(v.icon or "icon16/brick.png")

				if (v.multiOptions) then
					local options = isfunction(v.multiOptions) and v.multiOptions(itemTable, LocalPlayer()) or v.multiOptions

					for _, sub in pairs(options) do
						subMenu:AddOption(L(sub.name or "subOption"), function()
							itemTable.player = LocalPlayer()
								local send = true

								if (sub.OnClick) then
									send = sub.OnClick(itemTable)
								end

								if (sub.sound) then
									surface.PlaySound(sub.sound)
								end

								if (send != false) then
									InventoryAction(k, itemTable.id, inventory, sub.data)
								end
							itemTable.player = nil
						end)
					end
				end
			else
				menu:AddOption(L(v.name or k), function()
					itemTable.player = LocalPlayer()
						local send = true

						if (v.OnClick) then
							send = v.OnClick(itemTable)
						end

						if (v.sound) then
							surface.PlaySound(v.sound)
						end

						if (send != false) then
							InventoryAction(k, itemTable.id, inventory)
						end
					itemTable.player = nil
				end):SetImage(v.icon or "icon16/brick.png")
			end
		end

		-- we want drop to show up as the last option
		local info = itemTable.functions.drop

		if (info and info.OnCanRun and info.OnCanRun(itemTable) != false) then
			menu:AddOption(L(info.name or "drop"), function()
				itemTable.player = LocalPlayer()
					local send = true

					if (info.OnClick) then
						send = info.OnClick(itemTable)
					end

					if (info.sound) then
						surface.PlaySound(info.sound)
					end

					if (send != false) then
						InventoryAction("drop", itemTable.id, inventory)
					end
				itemTable.player = nil
			end):SetImage(info.icon or "icon16/brick.png")
		end

		menu:Open()
		for _, v in pairs(menu:GetChildren()[1]:GetChildren()) do
			v:SetFont("MenuFontNoClamp")
		end

		itemTable.player = nil
	end
end

local XYchanges = {
	[1] = {x = 1, y = 6},
	[2] = {x = 1, y = 7},
	[3] = {x = 1, y = 8},
	[4] = {x = 1, y = 9},
	[5] = {x = 1, y = 10},
}

function PANEL:OnDrop(bDragging, inventoryPanel, inventory, gridX, gridY)
	local item = self.itemTable

	if (!item or !bDragging) then
		return
	end

	if (inventory and inventory.vars and inventory.vars.isEquipSlots) then
		if (gridX == 5 and gridY <= 5) then
			gridX, gridY = XYchanges[gridY].x, XYchanges[gridY].y
		end
	end

	if (!IsValid(inventoryPanel)) then
		local inventoryID = self.inventoryID

		if (inventoryID) then
			InventoryAction("drop", item.id, inventoryID, {})
		end
	elseif (inventoryPanel:IsAllEmpty(gridX, gridY, item.width, item.height, self)) then
		local oldX, oldY = self.gridX, self.gridY

		if (oldX != gridX or oldY != gridY or self.inventoryID != inventoryPanel.invID) then
			self:Move(gridX, gridY, inventoryPanel)
		end
	elseif (inventoryPanel.combineItem) then
		local combineItem = inventoryPanel.combineItem
		local inventoryID = combineItem.invID

		if (inventoryID) then
			local info = combineItem.functions.combine

			if (info and info.OnCanRun and info.OnCanRun(combineItem, {item.id}) != false) then
				combineItem.player = LocalPlayer()

				if (combineItem.functions.combine.sound) then
					surface.PlaySound(combineItem.functions.combine.sound)
				end

				InventoryAction("combine", combineItem.id, inventoryID, {item.id})
				combineItem.player = nil
			end

			local info2 = item.functions.combined

			if (info2 and info2.OnCanRun and info2.OnCanRun(item, {combineItem.id}) != false) then
				item.player = LocalPlayer()
				InventoryAction("combined", item.id, inventoryID, {combineItem.id})
				item.player = nil
			end
		end
	end
end

function PANEL:Move(newX, newY, givenInventory, bNoSend)
	local iconSize2 = givenInventory.iconSize
	local oldX, oldY = self.gridX, self.gridY
	local oldParent = self:GetParent()

	if (givenInventory:OnTransfer(oldX, oldY, newX, newY, oldParent, bNoSend) == false) then
		return
	end

	local isEqSlots = givenInventory:IsEquipSlotsPanel()
	local x = (newX - 1 + (isEqSlots and newY > 5 and 4 or 0)) * iconSize2
	local y = (newY - 1 - (isEqSlots and newY > 5 and 5 or 0)) * iconSize2 + givenInventory:GetPadding(2)

	self.gridX = newX
	self.gridY = newY

	self:SetParent(givenInventory)
	self:SetPos(x, y)

	if (self.slots) then
		for _, v in ipairs(self.slots) do
			if (IsValid(v) and v.item == self) then
				v.item = nil
			end
		end
	end

	self.slots = {}

	for currentX = 1, self.gridW do
		for currentY = 1, self.gridH do
			local slot = givenInventory.slots[self.gridX + currentX - 1][self.gridY + currentY - 1]

			slot.item = self
			self.slots[#self.slots + 1] = slot
		end
	end

	local path = "willardnetworks/inventory/"
	local randomSounds = {"inv_move1.wav", "inv_move2.wav", "inv_move3.wav", "inv_move4.mp3", "inv_move5.mp3", "inv_move6.mp3"}
	surface.PlaySound(path .. table.Random(randomSounds))
end

function PANEL:PaintOver(width, height)
	local itemTable = self.itemTable

	if (itemTable and itemTable.PaintOver) then
		itemTable.PaintOver(self, itemTable, width, height)
	end
end

function PANEL:ExtraPaint(width, height) end

function PANEL:Paint(width, height)
	surface.SetDrawColor(0, 0, 0, 85)
	surface.DrawRect(2, 2, width - 0, height - 4)

	self:ExtraPaint(width, height)

	BaseClass.Paint(self, width, height)
end

vgui.Register("ixItemIconAdvanced", PANEL, "DModelPanel")

PANEL = {}

AccessorFunc(PANEL, "iconSize", "IconSize", FORCE_NUMBER)
AccessorFunc(PANEL, "bHighlighted", "Highlighted", FORCE_BOOL)

function PANEL:Init()
	self:SetIconSize(iconSize)
	self:Receiver(RECEIVER_NAME, self.ReceiveDrop)
	self:RequestFocus()

	self.Paint = function(this, w, h)
		render.ClearDepth() -- Fix for broken DModelPanel item icon depth
		surface.SetDrawColor(0, 0, 0, 85)
	end

	self.panels = {}
end

function PANEL:GetPadding(index)
	return select(index, self:GetDockPadding())
end

function PANEL:FitParent(invWidth, invHeight)
	local parent = self:GetParent()

	if (!IsValid(parent)) then
		return
	end

	local width, height = parent:GetSize()
	local padding = 4
	local iconSize2

	if (invWidth > invHeight) then
		iconSize2 = (width - padding * 2) / invWidth
	elseif (invHeight > invWidth) then
		iconSize2 = (height - padding * 2) / invHeight
	else
		-- we use height because the titlebar will make it more tall than it is wide
		iconSize2 = (height - padding * 2) / invHeight - 4
	end

	self:SetSize(iconSize2 * invWidth + padding * 2, iconSize2 * invHeight + padding * 2)
	self:SetIconSize(iconSize2)
end

function PANEL:OnRemove()
	if (self.childPanels) then
		for _, v in ipairs(self.childPanels) do
			if (v != self) then
				v:Remove()
			end
		end
	end
end

function PANEL:ViewOnly()
	self.viewOnly = true

	for _, icon in pairs(self.panels) do
		icon.OnMousePressed = nil
		icon.OnMouseReleased = nil
		icon.doRightClick = nil
	end
end

function PANEL:SetInventory(inventory, bFitParent)
	if (inventory.slots) then
		local invWidth, invHeight = inventory:GetSize()
		self.invID = inventory:GetID()

		if (IsValid(ix.gui.inv1) and ix.gui.inv1.childPanels and inventory != LocalPlayer():GetCharacter():GetInventory()) then
			self:SetIconSize(ix.gui.inv1:GetIconSize())
			self:SetPaintedManually(true)
			self.bNoBackgroundBlur = true

			ix.gui.inv1.childPanels[#ix.gui.inv1.childPanels + 1] = self
		elseif (bFitParent) then
			self:FitParent(invWidth, invHeight)
		else
			self:SetSize(self.iconSize, self.iconSize)
		end

		self:SetGridSize(invWidth, invHeight)

		for x, items in pairs(inventory.slots) do
			for y, data in pairs(items) do
				if (!data.id) then continue end

				local item = ix.item.instances[data.id]

				if (item and !IsValid(self.panels[item.id])) then
					local icon = self:AddIcon(item, item:GetModel() or "models/props_junk/popcan01a.mdl",
						x, y, item.width, item.height, item:GetSkin(), item:GetModelBodygroups(), item.color, item.material, item.rotate, item.OnInventoryDraw)

					if (IsValid(icon)) then
						icon:SetHelixTooltip(function(tooltip)
							ix.hud.PopulateItemTooltip(tooltip, item)
						end)

						self.panels[item.id] = icon
					end
				end
			end
		end
	end
end

function PANEL:OnKeyCodePressed(key)
	local hoveredPanel = vgui.GetHoveredPanel()

	if (hoveredPanel and (hoveredPanel:GetName() == "ixItemIcon" or hoveredPanel:GetName() == "ixItemIconAdvanced")) then
		if (key == KEY_Q) then
			hoveredPanel:OnDrop(true)
		elseif (key == KEY_R) then
			local itemTable = hoveredPanel:GetItemTable()
			local itemBase = itemTable.base

			if (itemBase == "base_ammo") then
				InventoryAction("use", itemTable.id, itemTable.invID)
			end
		end
	end
end

function PANEL:SetGridSize(w, h)
	local iconSize2 = self.iconSize
	local newWidth = w * iconSize2
	local newHeight = h * iconSize2 + self:GetPadding(2) + self:GetPadding(4)

	self.gridW = w
	self.gridH = h

	self:SetSize(newWidth, newHeight)
	self:BuildSlots()
end

function PANEL:PerformLayout(width, height)
	if (self.Sizing and self.gridW and self.gridH) then
		local newWidth = (width - 8) / self.gridW
		local newHeight = (height - self:GetPadding(2) + self:GetPadding(4)) / self.gridH

		self:SetIconSize((newWidth + newHeight) / 2)
		self:RebuildItems()
	end
end

function PANEL:BuildSlots()
	local iconSize2 = self.iconSize

	self.slots = self.slots or {}

	local function PaintSlot(slot, w, h)
		surface.SetDrawColor(35, 35, 35, 85)
		surface.DrawRect(1, 1, w - SScaleMin(2 / 3), h - SScaleMin(2 / 3))

		surface.SetDrawColor(80, 80, 80, 255)
		surface.DrawOutlinedRect(1, 1, w - SScaleMin(2 / 3), h - SScaleMin(2 / 3))
	end

	for _, v in ipairs(self.slots) do
		for _, v2 in ipairs(v) do
			v2:Remove()
		end
	end

	self.slots = {}

	for x = 1, self.gridW do
		self.slots[x] = {}

		for y = 1, self.gridH do
			local slot = self:Add("DPanel")
			slot:SetZPos(-999)
			slot.gridX = x
			slot.gridY = y
			slot:SetPos((x - 1) * iconSize2, (y - 1) * iconSize2 + self:GetPadding(2))
			slot:SetSize(iconSize2, iconSize2)
			slot.Paint = PaintSlot

			self.slots[x][y] = slot
		end
	end
end

function PANEL:RebuildItems()
	local iconSize2 = self.iconSize

	for x = 1, self.gridW do
		for y = 1, self.gridH do
			local slot = self.slots[x][y]

			slot:SetPos((x - 1) * iconSize2, (y - 1) * iconSize2 + self:GetPadding(2))
			slot:SetSize(iconSize2, iconSize2)
		end
	end

	for _, v in pairs(self.panels) do
		if (IsValid(v)) then
			v:SetPos(self.slots[v.gridX][v.gridY]:GetPos())
			v:SetSize(v.gridW * iconSize2, v.gridH * iconSize2)
		end
	end
end

function PANEL:PaintDragPreview(width, height, mouseX, mouseY, itemPanel)
	local iconSize2 = self.iconSize
	local item = itemPanel:GetItemTable()

	if (item) then
		local inventory = ix.item.inventories[self.invID]
		local dropX = math.ceil((mouseX - 0 - (itemPanel.gridW - 1) * 32) / iconSize2)
		local dropY = math.ceil((mouseY - self:GetPadding(2) - (itemPanel.gridH - 1) * 32) / iconSize2)

		local hoveredPanel = vgui.GetHoveredPanel()

		if (IsValid(hoveredPanel) and hoveredPanel != itemPanel and hoveredPanel.GetItemTable) then
			local hoveredItem = hoveredPanel:GetItemTable()

			if (hoveredItem) then
				local info = hoveredItem.functions.combine
				local info2 = item.functions.combined

				if ((info and info.OnCanRun and info.OnCanRun(hoveredItem, {item.id}) != false) or (info2 and info2.OnCanRun and info2.OnCanRun(item, {hoveredItem.id}) != false)) then
					surface.SetDrawColor(ColorAlpha(derma.GetColor("Info", self, Color(200, 0, 0)), 20))
					surface.DrawRect(
						hoveredPanel.x,
						hoveredPanel.y,
						hoveredPanel:GetWide(),
						hoveredPanel:GetTall()
					)

					self.combineItem = hoveredItem

					return
				end
			end
		end

		self.combineItem = nil
		local isEquipSlots = inventory.vars and inventory.vars.isEquipSlots or false

		-- don't draw grid if we're dragging it out of bounds
		if (inventory) then
			local invWidth, invHeight = inventory:GetSize()
			if (dropX < 1 or dropY < 1 or
				dropX - (isEquipSlots and dropX == 5 and 4 or 0) + itemPanel.gridW - 1 > invWidth or
				dropY + itemPanel.gridH - 1 > invHeight) then
				return
			end
		end

		for x = 0, itemPanel.gridW - 1 do
			for y = 0, itemPanel.gridH - 1 do
				local x2, y2 = dropX + x, dropY + y
				local eqX2, eqY2 = (!isEquipSlots and x2 or 1), (!isEquipSlots and y2 or x2 == 5 and y2 + 5 or y2)

				local bEmpty = self:IsEmpty(eqX2, eqY2, itemPanel)
				local canMove = (hook.Run("CanMoveItemSameInv", item, inventory, eqX2, eqY2) == true)

				if (isEquipSlots and canMove and bEmpty) then
					surface.SetDrawColor(0, 255, 0, 10)
				elseif (!isEquipSlots and bEmpty) then
					surface.SetDrawColor(0, 255, 0, 10)
				else
					surface.SetDrawColor(255, 255, 0, 10)
				end

				surface.DrawRect((x2 - 1) * iconSize, (y2 - 1) * iconSize2 + self:GetPadding(2), iconSize2, iconSize2)
			end
		end
	end
end

function PANEL:PaintOver(width, height)
	local panel = self.previewPanel

	if (IsValid(panel)) then
		local itemPanel = (dragndrop.GetDroppable() or {})[1]

		if (IsValid(itemPanel)) then
			if not (self.slots) then
				return
			end

			self:PaintDragPreview(width, height, self.previewX, self.previewY, itemPanel)
		end
	end

	self.previewPanel = nil
end

function PANEL:IsEmpty(x, y, this)
	if (self.slots) then
		return (self.slots[x] and self.slots[x][y]) and (!IsValid(self.slots[x][y].item) or self.slots[x][y].item == this)
	end
end

function PANEL:IsAllEmpty(x, y, width, height, this)
	for x2 = 0, width - 1 do
		for y2 = 0, height - 1 do
			if (!self:IsEmpty(x + x2, y + y2, this)) then
				return false
			end
		end
	end

	return true
end

function PANEL:OnTransfer(oldX, oldY, x, y, oldInventory, noSend)
	local inventories = ix.item.inventories
	local inventory = inventories[oldInventory.invID]
	local inventory2 = inventories[self.invID]
	local item
	if (inventory) then
		item = inventory:GetItemAt(oldX, oldY)

		if (!item) then
			return false
		end

		if (hook.Run("CanTransferItem", item, inventories[oldInventory.invID], inventories[self.invID], x, y) == false) then
			return false, "notAllowed"
		end

		if (item.CanTransfer and
			item:CanTransfer(inventory, inventory != inventory2 and inventory2 or nil) == false) then
			return false
		end
	end

	if (!noSend) then
		net.Start("ixInventoryMove")
			net.WriteUInt(oldX, 6)
			net.WriteUInt(oldY, 6)
			net.WriteUInt(x, 6)
			net.WriteUInt(y, 6)
			net.WriteUInt(oldInventory.invID, 32)
			net.WriteUInt(self != oldInventory and self.invID or oldInventory.invID, 32)
		net.SendToServer()
	end

	if (inventory) then
		inventory.slots[oldX][oldY] = nil
	end

	if (item and inventory2) then
		inventory2.slots[x] = inventory2.slots[x] or {}
		inventory2.slots[x][y] = item
	end
end

function PANEL:IsEquipSlotsPanel()
	local inventory = ix.item.inventories[self.invID]
	if inventory and inventory.vars then
		if inventory.vars.isEquipSlots then
			return true
		end
	end

	return false
end

function PANEL:AddIcon(item, model, x, y, w, h, skin, bodygroups, color, material, bRotate, OnInventoryDraw)
	local iconSize2 = self.iconSize
	local advanced = color or material or bRotate or OnInventoryDraw

	w = w or 1
	h = h or 1

	if (self.slots[x] and self.slots[x][y]) then
		local panel = advanced and self:Add("ixItemIconAdvanced") or self:Add("ixItemIcon")
		panel:SetSize(w * iconSize2, h * iconSize2)
		panel:SetZPos(999)
		panel:InvalidateLayout(true)
		panel:SetModel(model, skin, bodygroups)
		panel:SetPos(self.slots[x][y]:GetPos())
		panel.gridX = x
		panel.gridY = y

		if (advanced) then
			local entity = panel:GetEntity()
			color = color or Color(255, 255, 255)

			if (color) then
				panel:SetColor(color)
			end

			if (material) then
				entity:SetMaterial(material)
			end

			if (skin) then
				entity:SetSkin(skin)
			end

			if (OnInventoryDraw) then
				OnInventoryDraw(item, entity)
			end

			panel.LayoutEntity = function(this, entity)
				if (ix.gui.menu and IsValid(ix.gui.menu)) then
					this:SetColor(ColorAlpha(color, ix.gui.menu:GetAlpha()))
				end

				if (!bRotate) then return end

				local angle = entity:GetAngles()
				angle.y = angle.y + FrameTime() * 30
				entity:SetAngles(angle)
			end
		end

		if (self:IsEquipSlotsPanel()) then
			if self.slots[1] then
				-- witness my error noia
				local x1W = self.slots[1][1] and self.slots[1][1].GetWide and self.slots[1][1]:GetWide()
				local x1Pos = self.slots[1][1] and self.slots[1][1].GetPos and self.slots[1][1]:GetPos()
				local _, newY = false, false
				if self.slots[1][y - 5] and self.slots[1][y - 5].GetPos then
					_, newY = self.slots[1][y - 5]:GetPos()
				end

				if (y > 5 and x == 1) and (x1W and x1Pos and newY) then
					panel:SetPos(x1Pos + (x1W * 4), newY)
				end
			end
		end

		panel.gridW = w
		panel.gridH = h

		local inventory = ix.item.inventories[self.invID]

		if (!inventory) then
			return
		end

		local itemTable = inventory:GetItemAt(panel.gridX, panel.gridY)

		panel:SetInventoryID(inventory:GetID())
		panel:SetItemTable(itemTable)

		if (self.panels[itemTable:GetID()]) then
			self.panels[itemTable:GetID()]:Remove()
		end

		if (advanced) then
			if (itemTable.iconCam) then
				panel:SetCamPos(itemTable.iconCam.pos)
				panel:SetFOV(itemTable.iconCam.fov)
				panel:SetLookAng(itemTable.iconCam.ang)
			else
				local entity = panel:GetEntity()
				local pos = entity:GetPos()
				local camData = PositionSpawnIcon(entity, pos)

				if (camData) then
					panel:SetCamPos(camData.origin)
					panel:SetFOV(camData.fov)
					panel:SetLookAng(camData.angles)
				end
			end
		else
			if (itemTable.exRender) then
				panel.Icon:SetVisible(false)
				panel.ExtraPaint = function(this, panelX, panelY)
					local exIcon = ikon:GetIcon(itemTable.uniqueID)
					if (exIcon) then
						surface.SetMaterial(exIcon)
						surface.SetDrawColor(color_white)
						surface.DrawTexturedRect(0, 0, panelX, panelY)
					else
						ikon:renderIcon(
							itemTable.uniqueID,
							itemTable.width,
							itemTable.height,
							itemTable:GetModel(),
							itemTable.iconCam
						)
					end
				end
			else
				-- yeah..
				RenderNewIcon(panel, itemTable)
			end
		end

		panel.slots = {}

		for i = 0, w - 1 do
			for i2 = 0, h - 1 do
				local slot = self.slots[x + i] and self.slots[x + i][y + i2]

				if (IsValid(slot)) then
					slot.item = panel
					panel.slots[#panel.slots + 1] = slot
				else
					for _, v in ipairs(panel.slots) do
						v.item = nil
					end

					panel:Remove()

					return
				end
			end
		end

		return panel
	end
end

function PANEL:ReceiveDrop(panels, bDropped, menuIndex, x, y)
	local panel = panels[1]

	if (!IsValid(panel)) then
		self.previewPanel = nil
		return
	end

	if (bDropped) then
		local inventory = ix.item.inventories[self.invID]

		if (inventory and panel.OnDrop) then
			local dropX = math.ceil((x - 0 - (panel.gridW - 1) * 32) / self.iconSize)
			local dropY = math.ceil((y - self:GetPadding(2) - (panel.gridH - 1) * 32) / self.iconSize)

			panel:OnDrop(true, self, inventory, dropX, dropY)
		end

		self.previewPanel = nil
	else
		self.previewPanel = panel
		self.previewX = x
		self.previewY = y
	end
end

function PANEL:CreateNoSlots(backpacks, type)
	if backpacks.slots then
		for _, v in pairs(backpacks.slots) do
			if IsValid(v) then
				v:Remove()
			end
		end
	end

	backpacks.slots = {}

	if type == "noSlots" then return end

	for x = 1, (!type and 7 or type == "noLarge" and 4 or 3) do
		for y = 1, 4 do
			local noSlot = backpacks:Add("Panel")
			noSlot:SetPos((x - 1) * iconSize + (iconSize * self:GetNoSlotsXPosMultiplier(type)), (y - 1) * iconSize + SScaleMin(30 / 3))
			noSlot:SetSize(iconSize, iconSize)

			noSlot.Paint = PaintLockedSlot

			backpacks.slots[#backpacks.slots + 1] = noSlot
		end
	end
end

function PANEL:UpdateNoSlots()
	local backpacks = ix.gui.menuInventoryParent.backpacks
	if !backpacks or backpacks and !IsValid(backpacks) then return end

	local char = LocalPlayer():GetCharacter()
	if !char then return end

	local inventory = char:GetInventory()
	if !inventory then return end

	local hasSmallBag = inventory:HasItem("smallbag")
	local hasLargeBag = inventory:HasItem("largebag")

	local smallBag = hasSmallBag and self:GetIsInEquipSlots(char, hasSmallBag)
	local largeBag = hasLargeBag and self:GetIsInEquipSlots(char, hasLargeBag)

	if (!smallBag and !largeBag) then
		self:CreateNoSlots(backpacks)
	elseif largeBag and !smallBag then
		self:CreateNoSlots(backpacks, "noSmall")
	elseif !largeBag and smallBag then
		self:CreateNoSlots(backpacks, "noLarge")
	elseif largeBag and smallBag then
		self:CreateNoSlots(backpacks, "noSlots")
	end
end

function PANEL:GetIsInEquipSlots(char, item)
	local eqSlots = char:GetEquipInventory()
	if !eqSlots then return false end

	if !item.invID then return false end
	if item.invID != eqSlots then return false end

	return true
end

function PANEL:GetNoSlotsXPosMultiplier(type)
	return (!type and 0 or type == "noLarge" and 3 or 4)
end

vgui.Register("ixInventory", PANEL, "Panel")

hook.Add("CreateMenuButtons", "ixInventory", function(tabs)
	if (hook.Run("CanPlayerViewInventory") == false) then
		return
	end

	tabs["inv"] = {
		bDefault = true,

		RowNumber = 2,

		Width = 19,

		Height = 17,

		Icon = "willardnetworks/tabmenu/navicons/inventory.png",

		Create = function(info, tempCont)
			local titlePushDown = SScaleMin(30 / 3)
			local padding = SScaleMin(30 / 3)
			local topPushDown = SScaleMin(150 / 3)
			local scale780 = SScaleMin(780 / 3)
			local scale120 = SScaleMin(120 / 3)

			tempCont:SetSize(ScrW(), ScrH())

			local newCont = tempCont:Add("Panel")
			newCont:SetSize(ScrW() - (topPushDown * 2), scale120 + scale780 + titlePushDown)
			newCont:Center()

			local sizeXtitle, sizeYtitle = newCont:GetWide(), scale120
			local sizeXcontent, sizeYcontent = newCont:GetWide(), (scale780)

			newCont.titlePanel = newCont:Add("Panel")
			newCont.titlePanel:SetSize(sizeXtitle, sizeYtitle)
			newCont.titlePanel:SetPos(newCont:GetWide() * 0.5 - newCont.titlePanel:GetWide() * 0.5)

			local invTitleIcon = newCont.titlePanel:Add("DImage")
			invTitleIcon:SetImage("willardnetworks/tabmenu/navicons/inventory.png")
			invTitleIcon:SetSize(SScaleMin(19 / 3), SScaleMin(17 / 3))

			local invTitle = newCont.titlePanel:Add("DLabel")
			invTitle:SetFont("TitlesFontNoClamp")
			invTitle:SetText("Inventaire")
			invTitle:SizeToContents()
			invTitle:SetPos(SScaleMin(27 / 3), (SScaleMin(17 / 3) - invTitle:GetTall()) * 0.5)

			newCont.contentFrame = newCont:Add("Panel")
			newCont.contentFrame:SetSize(sizeXcontent, sizeYcontent)
			newCont.contentFrame:SetPos(newCont:GetWide() * 0.5 - newCont.contentFrame:GetWide() * 0.5, titlePushDown)

			newCont.invFrame = newCont.contentFrame:Add("Panel")
			newCont.invFrame:SetSize(newCont.contentFrame:GetWide() * 0.5 - padding, newCont.contentFrame:GetTall())
			newCont.invFrame:Dock(LEFT)
			newCont.invFrame:DockMargin(0, 0, padding, 0)

			newCont.backpacks = newCont.invFrame:Add("Panel")
			newCont.backpacks:SetTall(((iconSize + SScaleMin(2 / 3)) * 4) + SScaleMin(21 / 3))
			newCont.backpacks:Dock(BOTTOM)

			ix.gui.menuInventoryParent = newCont

			local canvas = newCont.invFrame:Add("DTileLayout")
			local canvasLayout = canvas.PerformLayout
			canvas.PerformLayout = nil -- we'll layout after we add the panels instead of each time one is added
			canvas:SetBorder(0)
			canvas:SetSpaceX(2)
			canvas:SetSpaceY(2)
			canvas:Dock(FILL)

			ix.gui.menuInventorynewCont = canvas

			local panel = canvas:Add("ixInventory")
			panel.bNoBackgroundBlur = true
			panel.childPanels = {}

			local char = LocalPlayer():GetCharacter()
			local inventory = char:GetInventory()

			if (inventory) then
				panel:SetInventory(inventory)
			end

			ix.gui.inv1 = panel
			ix.gui.inv1:UpdateNoSlots()

			local bFoundLarge, bFoundSmall = false, false
			for _, v in SortedPairsByMemberValue(inventory:GetItemsByBase("base_containerbags", true), "id") do
				if v.invID and char.GetEquipInventory and v.invID != char:GetEquipInventory() then continue end
				if (!v.isBag) then
					continue
				end

				if (v.name != "Sac" and v.name != "Petite sacoche") then
					continue
				elseif (v.name == "Sac") then
					if (bFoundLarge) then
						continue
					else
						bFoundLarge = true
					end
				elseif (v.name == "Petite sacoche") then
					if (bFoundSmall) then
						continue
					else
						bFoundSmall = true
					end
				end
				v.functions.View.OnClick(v)
			end

			canvas.PerformLayout = canvasLayout
			canvas:Layout()

			newCont.charFrame = newCont.contentFrame:Add("CharFrame")
			hook.Run("PostCharInfoFrameOpened", newCont.charFrame)

			local backspaceTitleIcon = newCont.backpacks:Add("DImage")
			backspaceTitleIcon:SetImage("willardnetworks/mainmenu/content.png")
			backspaceTitleIcon:SetSize(SScaleMin(16 / 3), SScaleMin(16 / 3))
			backspaceTitleIcon:SetPos(SScaleMin(1 / 3), SScaleMin(2 / 3))

			local backspaceTitle = newCont.backpacks:Add("DLabel")
			backspaceTitle:SetFont("SmallerTitleFontNoClamp")
			backspaceTitle:SetText("Sac à dos")
			backspaceTitle:SizeToContents()
			backspaceTitle:SetPos(SScaleMin(25 / 3))
		end
	}
end)

hook.Add("PostRenderVGUI", "ixInvHelper", function()
	local pnl = ix.gui.inv1

	hook.Run("PostDrawInventory", pnl)
end)
