---@class StdUi
local StdUi = LibStub and LibStub('StdUi', true)
if not StdUi then return end

local module, version = 'Dropdown', 4
if not StdUi:UpgradeNeeded(module, version) then return end

local TableInsert = tinsert

-- reference to all other dropdowns to close them when new one opens
local dropdowns = StdUiDropdowns or {}
StdUiDropdowns = dropdowns

local DropdownItemOnClick = function(self)
	self.dropdown:SetValue(self.value, self:GetText())
	self.dropdown.optsFrame:Hide()
end

local DropdownItemOnValueChanged = function(checkbox, isChecked)
	checkbox.dropdown:ToggleValue(checkbox.value, isChecked)
end

local DropdownMethods = {
	buttonCreate = function(parent)
		local dropdown = parent.dropdown
		local optionButton

		if dropdown.multi then
			optionButton = dropdown.stdUi:Checkbox(parent, '', parent:GetWidth(), 20)
		else
			optionButton = dropdown.stdUi:HighlightButton(parent, parent:GetWidth(), 20, '')
			optionButton.text:SetJustifyH('LEFT')
		end

		optionButton.dropdown = dropdown
		optionButton:SetFrameLevel(parent:GetFrameLevel() + 2)
		if not dropdown.multi then
			optionButton:SetScript('OnClick', DropdownItemOnClick)
		else
			optionButton.OnValueChanged = DropdownItemOnValueChanged
		end

		return optionButton
	end,

	buttonUpdate = function(parent, itemFrame, data)
		itemFrame:SetWidth(parent:GetWidth())
		itemFrame:SetText(data.text)

		if itemFrame.dropdown.multi then
			itemFrame:SetValue(data.value)
		else
			itemFrame.value = data.value
		end
	end,

	ShowOptions = function(self)
		for i = 1, #dropdowns do
			dropdowns[i]:HideOptions()
		end

		self.optsFrame:UpdateSize(self:GetWidth(), self.optsFrame:GetHeight())
		self.optsFrame:Show()
		self.optsFrame:Update()
		self:RepaintOptions()
	end,

	HideOptions = function(self)
		self.optsFrame:Hide()
	end,

	ToggleOptions = function(self)
		if self.optsFrame:IsShown() then
			self:HideOptions()
		else
			self:ShowOptions()
		end
	end,

	SetPlaceholder = function(self, placeholderText)
		if self:GetText() == '' or self:GetText() == self.placeholder then self:SetText(placeholderText) end

		self.placeholder = placeholderText
	end,

	RepaintOptions = function(self)
		local scrollChild = self.optsFrame.scrollChild
		self.stdUi:ObjectList(scrollChild, scrollChild.items, self.buttonCreate, self.buttonUpdate, self.options)
		self.optsFrame:UpdateItemsCount(#self.options)
	end,

	SetOptions = function(self, newOptions)
		self.options = newOptions
		local optionsHeight = #newOptions * 20
		local scrollChild = self.optsFrame.scrollChild
		if not scrollChild.items then scrollChild.items = {} end

		self.optsFrame:SetHeight(math.min(optionsHeight + 4, 200))
		scrollChild:SetHeight(optionsHeight)

		self:RepaintOptions()
	end,

	ToggleValue = function(self, value, state)
		assert(self.multi, 'Single dropdown cannot have more than one value!')

		-- Treat is as associative array
		if self.assoc then
			self.value[value] = state
		else
			if state then
				-- we are toggling it on
				if not tContains(self.value, value) then TableInsert(self.value, value) end
			else
				-- we are removing it from table
				if tContains(self.value, value) then tDeleteItem(self.value, value) end
			end
		end

		self:SetValue(self.value)
	end,

	SetValue = function(self, value, text)
		self.value = value

		if text then
			self:SetText(text)
		else
			self:SetText(self:FindValueText(value))
		end

		if self.multi then
			for _, checkbox in pairs(self.optsFrame.scrollChild.items) do
				local isChecked = false
				if self.assoc then
					isChecked = self.value[checkbox.value]
				else
					isChecked = tContains(self.value, checkbox.value)
				end

				checkbox:SetChecked(isChecked, true)
			end
		end

		if self.OnValueChanged then self.OnValueChanged(self, value, self:GetText()) end
	end,

	GetValue = function(self)
		return self.value
	end,

	FindValueText = function(self, value)
		if type(value) ~= 'table' then
			for i = 1, #self.options do
				local opt = self.options[i]

				if opt.value == value then return opt.text end
			end

			return self.placeholder or ''
		else
			local result = ''

			for i = 1, #self.options do
				local opt = self.options[i]

				if self.assoc then
					for key, checked in pairs(value) do
						if checked and key == opt.value then
							if result == '' then
								result = opt.text
							else
								result = result .. ', ' .. opt.text
							end
						end
					end
				else
					for x = 1, #value do
						if value[x] == opt.value then
							if result == '' then
								result = opt.text
							else
								result = result .. ', ' .. opt.text
							end
						end
					end
				end
			end

			if result ~= '' then
				return result
			else
				return self.placeholder or ''
			end
		end
	end,
}

local DropdownEvents = {
	OnClick = function(self)
		self:ToggleOptions()
	end,
}

--- Creates a single level dropdown menu
--- local options = {
---		{text = 'some text', value = 10},
---		{text = 'some text2', value = 11},
---		{text = 'some text3', value = 12},
--- }
---@return Dropdown
function StdUi:Dropdown(parent, width, height, options, value, multi, assoc)
	---@class Dropdown : Button
	local dropdown = self:Button(parent, width, height, '')
	dropdown.stdUi = self

	dropdown.text:SetJustifyH('LEFT')
	-- make it shorter because of arrow
	dropdown.text:ClearAllPoints()
	self:GlueAcross(dropdown.text, dropdown, 2, -2, -16, 2)

	local dropTex = self:Texture(dropdown, 15, 15, [[Interface\Buttons\SquareButtonTextures]])
	dropTex:SetTexCoord(0.45312500, 0.64062500, 0.20312500, 0.01562500)
	self:GlueRight(dropTex, dropdown, -2, 0, true)

	local optsFrame = self:FauxScrollFrame(dropdown, dropdown:GetWidth(), 200, 10, 20)
	optsFrame:Hide()
	self:GlueBelow(optsFrame, dropdown, 0, 1, 'LEFT')
	dropdown:SetFrameLevel(optsFrame:GetFrameLevel() + 1)

	dropdown.multi = multi
	dropdown.assoc = assoc

	dropdown.optsFrame = optsFrame
	dropdown.dropTex = dropTex
	dropdown.options = options

	optsFrame.scrollChild.dropdown = dropdown

	for k, v in pairs(DropdownMethods) do
		dropdown[k] = v
	end

	if options then dropdown:SetOptions(options) end

	if value then
		dropdown:SetValue(value)
	elseif multi then
		dropdown.value = {}
	end

	for k, v in pairs(DropdownEvents) do
		dropdown:SetScript(k, v)
	end

	TableInsert(dropdowns, dropdown)

	return dropdown
end

StdUi:RegisterModule(module, version)
