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

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

local pairs = pairs
local strlen = strlen

----------------------------------------------------
--- SimpleEditBox
----------------------------------------------------

local SimpleEditBoxMethods = {
	SetFontSize = function(self, newSize)
		self:SetFont(self:GetFont(), newSize, self.stdUi.config.font.effect)
	end,
}

local SimpleEditBoxEvents = {
	OnEscapePressed = function(self)
		self:ClearFocus()
	end,
}

---@return EditBox
function StdUi:SimpleEditBox(parent, width, height, text)
	---@type EditBox
	local editBox = CreateFrame('EditBox', nil, parent)
	editBox.stdUi = self
	self:InitWidget(editBox)

	editBox:SetTextInsets(3, 3, 3, 3)
	editBox:SetFontObject(ChatFontNormal)
	editBox:SetAutoFocus(false)

	for k, v in pairs(SimpleEditBoxMethods) do
		editBox[k] = v
	end

	for k, v in pairs(SimpleEditBoxEvents) do
		editBox:SetScript(k, v)
	end

	if text then editBox:SetText(text) end

	self:HookDisabledBackdrop(editBox)
	self:HookHoverBorder(editBox)
	self:ApplyBackdrop(editBox)
	self:SetObjSize(editBox, width, height)

	return editBox
end

----------------------------------------------------
--- ApplyPlaceholder
----------------------------------------------------

local ApplyPlaceholderOnTextChanged = function(self)
	if strlen(self:GetText()) > 0 then
		self.placeholder.icon:Hide()
		self.placeholder.label:Hide()
	else
		self.placeholder.icon:Show()
		self.placeholder.label:Show()
	end
end

function StdUi:ApplyPlaceholder(widget, placeholderText, icon, iconColor)
	widget.placeholder = {}

	local label = self:Label(widget, placeholderText)
	self:SetTextColor(label, 'disabled')
	widget.placeholder.label = label

	if icon then
		local texture = self:Texture(widget, 14, 14, icon)
		local c = iconColor or self.config.font.color.disabled
		texture:SetVertexColor(c.r, c.g, c.b, c.a)

		self:GlueLeft(texture, widget, 5, 0, true)
		self:GlueRight(label, texture, 2, 0)
		widget.placeholder.icon = texture
	else
		self:GlueLeft(label, widget, 2, 0, true)
	end

	widget:HookScript('OnTextChanged', ApplyPlaceholderOnTextChanged)
end

----------------------------------------------------
--- SearchEditBox
----------------------------------------------------

local SearchEditBoxOnTextChanged = function(self)
	if self.OnValueChanged then self:OnValueChanged(self:GetText()) end
end

function StdUi:SearchEditBox(parent, width, height, placeholderText)
	local editBox = self:SimpleEditBox(parent, width, height, '')

	editBox:SetScript('OnTextChanged', SearchEditBoxOnTextChanged)

	self:ApplyPlaceholder(editBox, placeholderText, [[Interface\Common\UI-Searchbox-Icon]])

	return editBox
end

----------------------------------------------------
--- SearchEditBox
----------------------------------------------------

local EditBoxMethods = {
	GetValue = function(self)
		return self.value
	end,

	SetValue = function(self, value)
		self.value = value
		self:SetText(value)
		self:Validate()
		self.button:Hide()
	end,

	IsValid = function(self)
		return self.isValid
	end,

	Validate = function(self)
		self.isValidated = true
		self.isValid = self.validator(self)

		if self.isValid then
			if self.button then self.button:Hide() end

			if self.OnValueChanged and tostring(self.lastValue) ~= tostring(self.value) then
				self:OnValueChanged(self.value)
				self.lastValue = self.value
			end
		end

		self.isValidated = false
	end,
}

local EditBoxButtonOnClick = function(self)
	self.editBox:Validate(self.editBox)
end

local EditBoxEvents = {
	OnEnterPressed = function(self)
		self:Validate()
	end,

	OnTextChanged = function(self, isUserInput)
		local value = StdUi.Util.stripColors(self:GetText())
		if tostring(value) ~= tostring(self.value) then
			if not self.isValidated and self.button and isUserInput then self.button:Show() end
		else
			self.button:Hide()
		end
	end,
}

---@return EditBox
function StdUi:EditBox(parent, width, height, text, validator)
	validator = validator or StdUi.Util.editBoxValidator

	local editBox = self:SimpleEditBox(parent, width, height, text)
	editBox.validator = validator

	local button = self:Button(editBox, 40, height - 4, OKAY)
	button:SetPoint('RIGHT', -2, 0)
	button:Hide()
	button.editBox = editBox
	editBox.button = button

	for k, v in pairs(EditBoxMethods) do
		editBox[k] = v
	end

	button:SetScript('OnClick', EditBoxButtonOnClick)

	for k, v in pairs(EditBoxEvents) do
		editBox:SetScript(k, v)
	end

	return editBox
end

----------------------------------------------------
--- SearchEditBox
----------------------------------------------------

local NumericBoxMethods = {
	SetMaxValue = function(self, value)
		self.maxValue = value
		self:Validate()
	end,

	SetMinValue = function(self, value)
		self.minValue = value
		self:Validate()
	end,

	SetMinMaxValue = function(self, min, max)
		self.minValue = min
		self.maxValue = max
		self:Validate()
	end,
}

function StdUi:NumericBox(parent, width, height, text, validator)
	validator = validator or self.Util.numericBoxValidator

	local editBox = self:EditBox(parent, width, height, text, validator)
	editBox:SetNumeric(true)

	for k, v in pairs(NumericBoxMethods) do
		editBox[k] = v
	end

	return editBox
end

----------------------------------------------------
--- MoneyBox
----------------------------------------------------

local MoneyBoxMethods = {
	SetValue = function(self, value)
		self.value = value
		local formatted = self.stdUi.Util.formatMoney(value)
		self:SetText(formatted)
		self:Validate()
		self.button:Hide()
	end,
}

function StdUi:MoneyBox(parent, width, height, text, validator, excludeCopper)
	if excludeCopper then
		validator = validator or self.Util.moneyBoxValidatorExC
	else
		validator = validator or self.Util.moneyBoxValidator
	end

	local editBox = self:EditBox(parent, width, height, text, validator)
	editBox.stdUi = self
	editBox:SetMaxLetters(20)

	for k, v in pairs(MoneyBoxMethods) do
		editBox[k] = v
	end

	return editBox
end

----------------------------------------------------
--- MultiLineBox
----------------------------------------------------

local MultiLineBoxMethods = {
	SetValue = function(self, value)
		self.editBox:SetText(value)

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

	GetValue = function(self)
		return self.editBox:GetText()
	end,

	SetFont = function(self, font, size, flags)
		self.editBox:SetFont(font, size, flags)
	end,

	Enable = function(self)
		self.editBox:Enable()
	end,

	Disable = function(self)
		self.editBox:Disable()
	end,

	SetFocus = function(self)
		self.editBox:SetFocus()
	end,

	ClearFocus = function(self)
		self.editBox:ClearFocus()
	end,

	HasFocus = function(self)
		return self.editBox:HasFocus()
	end,
}

local MultiLineBoxOnCursorChanged = function(self, _, y, _, cursorHeight)
	local sf, newY = self.scrollFrame, -y
	local offset = sf:GetVerticalScroll()

	if newY < offset then
		sf:SetVerticalScroll(newY)
	else
		newY = newY + cursorHeight - sf:GetHeight() + 6 --text insets
		if newY > offset then sf:SetVerticalScroll(math.ceil(newY)) end
	end
end

local MultiLineBoxOnTextChanged = function(self)
	if self.panel.OnValueChanged then self.panel.OnValueChanged(self.panel, self:GetText()) end
end

local MultiLineBoxScrollOnMouseDown = function(self, button)
	self.scrollChild:SetFocus()
end

local MultiLineBoxScrollOnVerticalScroll = function(self, offset)
	self.scrollChild:SetHitRectInsets(0, 0, offset, self.scrollChild:GetHeight() - offset - self:GetHeight())
end

---@return StdUi.MultiLineBox
function StdUi:MultiLineBox(parent, width, height, text)
	---@class StdUi.MultiLineBox : Frame, EditBox
	local editBox = CreateFrame('EditBox')
	local widget = self:ScrollFrame(parent, width, height, editBox)
	editBox.stdUi = self

	local scrollFrame = widget.scrollFrame
	scrollFrame.editBox = editBox
	widget.editBox = editBox
	editBox.panel = widget

	self:ApplyBackdrop(widget, 'button')
	self:HookHoverBorder(scrollFrame)
	self:HookHoverBorder(editBox)

	editBox:SetWidth(scrollFrame:GetWidth())
	self:GlueAcross(scrollFrame, widget, 2, -2, -widget.scrollBarWidth - 2, 3)
	--editBox:SetHeight(scrollFrame:GetHeight());

	editBox:SetTextInsets(3, 3, 3, 3)
	editBox:SetFontObject(ChatFontNormal)
	editBox:SetAutoFocus(false)
	editBox:SetScript('OnEscapePressed', editBox.ClearFocus)
	editBox:SetMultiLine(true)
	editBox:EnableMouse(true)
	editBox:SetAutoFocus(false)
	editBox:SetCountInvisibleLetters(false)
	editBox:SetAllPoints()

	editBox.scrollFrame = scrollFrame
	editBox.panel = widget

	for k, v in pairs(MultiLineBoxMethods) do
		widget[k] = v
	end

	if text then editBox:SetText(text) end

	editBox:SetScript('OnCursorChanged', MultiLineBoxOnCursorChanged)
	editBox:SetScript('OnTextChanged', MultiLineBoxOnTextChanged)

	scrollFrame:HookScript('OnMouseDown', MultiLineBoxScrollOnMouseDown)
	scrollFrame:HookScript('OnVerticalScroll', MultiLineBoxScrollOnVerticalScroll)

	widget.SetText = widget.SetValue
	widget.GetText = widget.GetValue

	return widget
end

StdUi:RegisterModule(module, version)
