-- Copyright © 2024 @Groick (Twitch). All rights reserved.
local function L(key, ...) return EasyOptions:Localize(key, ...) end
function EasyOptions:CreateDropDown(parent, customControlName, value, dropDownItemInfos, text, tooltipTitleText, tooltipText, positionInfo, customSize, customFontInfo, customTextColor, onClickHandler, onValueChangedHandler)
  local controlName = EasyOptions:GetControlName("DropDownParent", customControlName)
  local control = CreateFrame("Frame", controlName, parent) -- Acts as the control that is associated with the OptionItem. This is required because the WoW API can modify the initial text color of control.Label if control inherits from "UIDropDownMenuTemplate".
  control.Name = controlName
  control.Type = "DropDownParent"
  control.BaseType = "DropDownParent"
  -- EasyOptions:SetTooltip(control, control, tooltipTitleText, tooltipText)
  control:SetSize(1, 1) -- IMPORTANT: We must set the size because the WoW API hides controls (and the control's children) if the control size is set to its default value of (0, 0).
  control:EnableMouse(false)
  
  control.OnEnabledChangedHandler = function(self)
    -- Reserved.
  end
  EasyOptions:AddSupportForEnabledState(control)
  
  local controlName_DropDown = controlName..".DropDown"
  local dropDown = CreateFrame("Frame", controlName_DropDown, control, "UIDropDownMenuTemplate")
  dropDown.Name = controlName_DropDown
  dropDown.Type = "DropDown"
  dropDown.BaseType = "DropDown"
  EasyOptions:SetTooltip(dropDown, dropDown, tooltipTitleText, tooltipText)
  control.DropDown = dropDown
  EasyOptions:AddControlToOwner(dropDown, control)
  EasyOptions:AddControlToParent(dropDown, control)

  local defaultWidth = EasyOptions:GetDefaultDropDownWidth()
  local defaultHeight = 22
  local dropDownButtonWidth = EasyOptions:GetDropDownButtonWidth()
  local widthMaxAllowed = EasyOptions.Fields.ControlSizeMaxAllowed_Large
  local ignoreCustomHeight = true
  local width, height, hasWidth, hasHeight, shouldFillWidth = EasyOptions:GetSizeFromCustomSize(customSize, ignoreCustomHeight, defaultWidth, defaultHeight, widthMaxAllowed, EasyOptions.Fields.ControlSizeMaxAllowed_Large, true, false)
  local widthForSetWidth = math.max(width - dropDownButtonWidth), EasyOptions:IIF(width > 0, 1, 0) -- Subtract the size of the DropDown's button which does not seem to be accounted for by the WoW API and also limit the width to a minimum value of 1 unless the width is <= 0 in which case we allow a width of 0.
  UIDropDownMenu_SetWidth(dropDown, widthForSetWidth) -- IMPORTANT: We must use UIDropDownMenu_SetWidth rather than dropDown:SetSize(width, height).
  -- dropDown:SetSize(width, height)
  dropDown:SetHitRectInsets(0, dropDownButtonWidth, 0, 0) -- Left, Right, Top, Bottom. Negative values increase the size of the hit box and positive values decrease the size of the hit box. -- IMPORTANT: This is required because the WoW API sets the hit box of the DropDown to be wider than the graphics of the control (as if the DropDown's hit box was set assuming that the DropDown's button was positioned to the right of the control).
  
  dropDown.GetValue = function(self)
    return EasyOptions:GetDropDownSelectedValue(self)
  end
  dropDown.GetText = function(self)
    return EasyOptions:GetDropDownSelectedText(self)
  end
  dropDown.SetValue = function(self, value)
    EasyOptions:SetDropDownSelectedValue(self, value)
  end
  dropDown.ContainsValue = function(self, value) -- containsValue, textOfItemWithValue, index
    local valueLocal = EasyOptions:IIF(value == EasyOptions.Fields.EmptyDropDownValue, nil, value)
    return EasyOptions:ContainsValueInDropDownItemInfos(valueLocal, self.DropDownItemInfos)
  end
  dropDown.ContainsSelectedValue = function(self) -- containsValue, textOfItemWithValue, index
    local value = self:GetValue()
    return self:ContainsValue(value)
  end
  dropDown.ContainsText = function(self, text) -- containsText, valueOfItemWithText, index
    return EasyOptions:ContainsTextInDropDownItemInfos(text, self.DropDownItemInfos)
  end
  dropDown.ContainsSelectedText = function(self) -- containsText, valueOfItemWithText, index
    local text = self:GetText()
    return self:ContainsText(text)
  end
  dropDown.GetIndexOfSelectedValue = function(self)
    local containsValue, textOfItemWithValue, index = self:ContainsSelectedValue()
    return index
  end
  dropDown.GetValueToSelectAfterRemoveDropDownItem = function(self, dropDownItemInfosDesired) -- value, index
    return EasyOptions:GetValueToSelectAfterRemoveDropDownItem(self, dropDownItemInfosDesired)
  end
  dropDown.AddDropDownItem = function(self, value, text, promptUserIfItemValueExists, promptUserIfItemTextExists, allowShowErrorMessage, allowPrintErrorMessage)
    return EasyOptions:AddDropDownItem(self, value, text, promptUserIfItemValueExists, promptUserIfItemTextExists, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.RemoveDropDownItem = function(self, value, text, allowShowErrorMessage, allowPrintErrorMessage)
    return EasyOptions:RemoveDropDownItem(self, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.RemoveSelectedDropDownItem = function(self, allowShowErrorMessage, allowPrintErrorMessage)
    local value = self:GetValue()
    local text = self:GetText()
    return self:RemoveDropDownItem(value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.CanMoveDropDownItemToIndex = function(self, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
    return EasyOptions:CanMoveDropDownItemToIndex(self, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.MoveDropDownItem = function(self, value, text, indexDesired, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
    return EasyOptions:MoveDropDownItem(self, value, text, indexDesired, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.CanMoveDropDownItemUp = function(self, value, text, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
    return EasyOptions:CanMoveDropDownItemUp(self, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.MoveDropDownItemUp = function(self, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
    return EasyOptions:MoveDropDownItemUp(self, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.CanMoveSelectedDropDownItemUp = function(self, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
    local value = self:GetValue()
    local text = self:GetText()
    return self:CanMoveDropDownItemUp(value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.MoveSelectedDropDownItemUp = function(self, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
    local value = self:GetValue()
    local text = self:GetText()
    return self:MoveDropDownItemUp(value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.CanMoveDropDownItemDown = function(self, value, text, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
    return EasyOptions:CanMoveDropDownItemDown(self, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.MoveDropDownItemDown = function(self, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
    return EasyOptions:MoveDropDownItemDown(self, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.CanMoveSelectedDropDownItemDown = function(self, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
    local value = self:GetValue()
    local text = self:GetText()
    return self:CanMoveDropDownItemDown(value, text, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.MoveSelectedDropDownItemDown = function(self, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
    local value = self:GetValue()
    local text = self:GetText()
    return self:MoveDropDownItemDown(value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  end
  dropDown.SetDropDownItemInfos = function(self, dropDownItemInfos, defaultValue, allowSetToDefaultValue) -- Sets dropDown.DropDownItemInfos and sets the items in the DropDown menu to the items that are contained in dropDown.DropDownItemInfos.
    local dropDown = self
    dropDown.DropDownItemInfos = dropDownItemInfos -- Set dropDown.DropDownItemInfos.
    UIDropDownMenu_Initialize(dropDown, function(self, level, menuList) -- Set the items in the DropDown menu to the items that are contained in dropDown.DropDownItemInfos
      EasyOptions:CreateDropDown_AddItems(self, level, menuList)
    end)
    if (dropDown:ContainsValue(dropDown:GetValue())) then
      dropDown:SetValue(dropDown:GetValue()) -- Set the DropDown's value to its current value in order to update the DropDown's text.
    elseif (allowSetToDefaultValue) then
      dropDown:SetValue(defaultValue) -- Set the selected value to defaultValue if the DropDown no longer contains the selected value.
    end
    if (dropDown.IsEditableDropDown) then
      EasyOptions:UpdateEditableDropDownItemInfosInAddonDB(dropDown)
      EasyOptions:UpdateEditableDropDownControlStates(dropDown.Parent.Parent)
    end
  end
  dropDown.GetNumberOfDropDownItems = function(self)
    return EasyOptions:GetLength(self.DropDownItemInfos)
  end
  control.GetValue = function(self)
    return self.DropDown:GetValue()
  end
  control.SetValue = function(self, value)
    self.DropDown:SetValue(value)
  end
  
  -- Create the label for the control.
  local labelOffsetX = 17
  local labelOffsetY = 3
  local labelPositionInfo = {"BOTTOMLEFT", "TOPLEFT", labelOffsetX, labelOffsetY} -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local fontObjectName = EasyOptions.Fields.DefaultLabelFontObjectName
  local labelControl, label, labelControlRegionWidth, labelControlRegionHeight = EasyOptions:CreateLabelForControl(parent, control, control, controlName, text, tooltipTitleText, tooltipText, labelPositionInfo, fontObjectName, customFontInfo, customTextColor)
  label:SetJustifyH("LEFT") -- "LEFT", "RIGHT", "CENTER"
  label:SetJustifyV("MIDDLE") -- "TOP", "BOTTOM", "MIDDLE"
  local labelRegionWidth = 0 -- init
  local labelRegionHeight = 0 -- init
  local hasLabelText = (text ~= nil) and (text ~= "")
  if (hasLabelText) then
    labelRegionWidth = labelControlRegionWidth + 1 -- + labelOffsetX
    labelRegionHeight = labelControlRegionHeight + labelOffsetY + 1
  end
  
  labelRegionHeight = math.max(labelRegionHeight, 1)
  local offsetX = -15 -- Offset the x-position of this control type to help it align better with the other controls.
  local offsetY = 3 + labelRegionHeight -- Offset the y-position of this control type to help it align better with the other controls.
  control:ClearAllPoints()
  control:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3] + offsetX, positionInfo[4] - offsetY)
  if (shouldFillWidth) then -- NOTE: The WoW API DropDown control type does not support adjusting the control's width by setting a point like control:SetPoint("RIGHT", parent, "RIGHT", x, y). This limitation is probably related to the need to use the WoW API method UIDropDownMenu_SetWidth to set the width of the DropDown control.
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("is a DropDown that has a width of \"{1}\" which is not supported due to a limitation of the API.", EasyOptions.Fields.FillWidthFlag))
    -- EasyOptions:SetControlPointToFillWidth(control, parent, positionInfo, 2, 2)
  end
  --
  dropDown:ClearAllPoints()
  dropDown:SetPoint("TOPLEFT", control, "TOPLEFT", 0.0, 0.0) -- NOTE: The WoW API DropDown control type does not support adjusting the control's width by setting a point like control:SetPoint("RIGHT", parent, "RIGHT", x, y). This limitation is probably related to the need to use the WoW API method UIDropDownMenu_SetWidth to set the width of the DropDown control.
  
  -- Initialize the DropDown items and set the value of the DropDown control.
  dropDown:SetDropDownItemInfos(dropDownItemInfos, value, true)

  dropDown.OnClickHandler = onClickHandler
  -- dropDown:SetScript("OnClick", onClickHandler)
  dropDown.OnValueChangedHandler = onValueChangedHandler
  -- dropDown:SetScript("OnValueChanged", onValueChangedHandler)
  dropDown.OnEnabledChangedHandler = function(self)
    if (self.IsEnabled == nil) or (self:IsEnabled()) then -- Assume that the control is enabled if its IsEnabled method is nil.
      EasyOptions:SetToDefaultTextColor(self)
      UIDropDownMenu_EnableDropDown(self)
    else
      EasyOptions:SetToDisabledTextColor(self)
      UIDropDownMenu_DisableDropDown(self)
    end
  end
  EasyOptions:AddSupportForEnabledState(dropDown)
  
  local widthEffective = math.max(EasyOptions:GetDropDownWidthEffective(width), labelRegionWidth)
  local heightEffective = height + labelRegionHeight + 4
  
  local controlRegionWidth = widthEffective
  local controlRegionHeight = heightEffective
  control.ControlRegionWidth = controlRegionWidth
  control.ControlRegionHeight = controlRegionHeight
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateDropDown_AddItems(control, level, menuList)
  -- local optionItem = control.OptionItem
  -- if (optionItem ~= nil) then
  --   EasyOptions:Print("Add DropDown Items"..EasyOptions:FormatItems({optionItem.Name, control.Type, level, menuList}))
  -- end
  
  local dropDownItemInfos = control.DropDownItemInfos
  if (dropDownItemInfos ~= nil) and (type(dropDownItemInfos) ~= "table") then
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for its DropDown items. DropDown items must be contained in a table."))
    return
  end
  
  local info = UIDropDownMenu_CreateInfo()
  local length = EasyOptions:GetLength(dropDownItemInfos)
  local value
  local text
  local tooltipTitleText
  local tooltipText
  for i = 1, length do
    value, text, tooltipTitleText, tooltipText = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfos[i])
    info.value = value
    info.text = text
    info.arg1 = text -- NOTE: We set info.arg1 = text because info.text is always set to nil when EasyOptions:CreateDropDown_OnClickHandler is called.
    info.arg2 = control
    info.tooltipTitle = tooltipTitleText
    info.tooltipText = tooltipText
    info.tooltipOnButton = true
    info.tooltipWhileDisabled = false
    -- EasyOptions:PrintItems({"Item Info", i, text, control, info.arg1, info.arg2})
    info.checked = false
    info.func = function(self, arg1, arg2, checked)
      EasyOptions:CreateDropDown_OnClickHandler(self, arg1, arg2, checked)
      if (control.OnClickHandler ~= nil) then
        control:OnClickHandler(self, arg1, arg2, checked)
      end
    end
    -- EasyOptions:Print("Add Item "..EasyOptions:Format(i)..": "..EasyOptions:Format(info.value)..", "..EasyOptions:Format(info.text)..", "..EasyOptions:Format(info.arg1)..", "..EasyOptions:Format(info.arg2)..", "..EasyOptions:Format(info.checked)..", "..EasyOptions:Format(info.func))
    UIDropDownMenu_AddButton(info)
  end
end
function EasyOptions:CreateDropDown_OnClickHandler(info, arg1, arg2, checked)
  local value = info.value
  local text = info.arg1 -- NOTE: We don't use info.text because info.text is always set to nil when this method is called.
  local control = info.arg2
  control:SetValue(value)
end
function EasyOptions:CreateOptionDropDown(parent, positionInfo, optionItem)
  local optionName = optionItem.Name
  local value = EasyOptions:GetOptionValueOrDefaultValue(optionName, optionItem.DefaultValue)
  local dropDownItemInfos = optionItem.DropDownItemInfos
  local text = EasyOptions:GetControlText(optionItem)
  local tooltipTitleText = optionItem.TooltipTitleText or text
  local tooltipText = optionItem.TooltipText
  local customSize = optionItem.Width
  local customFontInfo = optionItem.FontInfo
  local customTextColor = optionItem.TextColor
  local onClickHandler = optionItem.OnClickHandler -- See also EasyOptions:CreateDropDown_AddItems_FromDropDownItemInfos and EasyOptions:CreateDropDown_OnClickHandler.
  local onValueChangedHandlerCustom = optionItem.OnValueChangedHandler
  local onValueChangedHandler = function(self)
    if (onValueChangedHandlerCustom ~= nil) then
      onValueChangedHandlerCustom(self)
    end
  end
  
  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateDropDown(parent, nil, value, dropDownItemInfos, text, tooltipTitleText, tooltipText, positionInfo, customSize, customFontInfo, customTextColor, onClickHandler, onValueChangedHandler)
  -- control:ClearAllPoints()
  -- control:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3], positionInfo[4])
  EasyOptions:AddRightButtonDownHandlerForOptionControl(control)
  EasyOptions:AddRightButtonDownHandlerForOptionControl(control.DropDown)
  
  optionItem.Control = control
  control.OptionItem = optionItem
  control.DropDown.OptionItem = optionItem
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:GetDropDownSelectedValue(control)
  if (control == nil) then
    return nil
  end
  local result = UIDropDownMenu_GetSelectedValue(control)
  if (result == nil) then
    result = EasyOptions.Fields.EmptyDropDownValue
  end
  return result
end
function EasyOptions:GetDropDownSelectedText(control)
  if (control == nil) then
    return nil
  end
  local index = control:GetIndexOfSelectedValue()
  if (index == nil) then
    return nil
  end
  local dropDownItemInfo = control.DropDownItemInfos[index]
  local value, text, tooltipTitleText, tooltipText = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfo)
  return text
end
function EasyOptions:SetDropDownSelectedValue(control, value)
  local valueLocal = EasyOptions:IIF(value == EasyOptions.Fields.EmptyDropDownValue, nil, value)
  if (control.OptionItem ~= nil) then
    EasyOptions:CreateDropDown_AddItems(control, nil, nil) -- This is required so the appropriate items are added to the DropDown before we set the valueLocal. Otherwise, if no items have been added to the DropDown yet then the DropDown's text will be set to "Custom" when we set the DropDown's valueLocal. Do the following to reproduce this issue: 1. Comment out this case. 2. Set the valueLocal of a DropDown to a valueLocal that is different than the DropDown's default valueLocal. 3. Reload the UI. 4. Right click the DropDown and choose to reset the DropDown to its default valueLocal.
  end
  UIDropDownMenu_SetSelectedValue(control, valueLocal, false)
  if (valueLocal == nil) then
    UIDropDownMenu_SetText(control, "") -- Set the text to an empty string if the valueLocal is nil so the DropDown does not display "Custom" as its text.
  end
  -- EasyOptions:Print("DropDown value set to "..EasyOptions:Format(valueLocal))
  
  -- Update Options (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) then
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, value, allowUpdateControl)
  end

  if (control.OnValueChangedHandler ~= nil) then
    control:OnValueChangedHandler(control, value)
  end
end
function EasyOptions:ContainsValueInDropDownItemInfos(value, dropDownItemInfos) -- containsValue, textOfItemWithValue, index
  if (value == nil) then -- IMPORTANT: DropDowns and DropDownItemInfos do not support a value of nil.
    return false, nil, nil
  end

  local length = EasyOptions:GetLength(dropDownItemInfos)
  local valueCurrent
  local textCurrent
  local tooltipTitleTextCurrent
  local tooltipTextCurrent
  local allowAreApproximatelyEqual = false -- Require an exact match when comparing DropDown item values.
  for i = 1, length do
    valueCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfos[i])
    if (EasyOptions:AreEqual(valueCurrent, value, allowAreApproximatelyEqual)) then
      return true, textCurrent, i
    end
  end
  return false, nil, nil
end
function EasyOptions:ContainsTextInDropDownItemInfos(text, dropDownItemInfos) -- containsText, valueOfItemWithText, index
  if (text == nil) then -- IMPORTANT: DropDowns and DropDownItemInfos do not support a text value of nil.
    return false, nil, nil
  end

  local length = EasyOptions:GetLength(dropDownItemInfos)
  local valueCurrent
  local textCurrent
  local tooltipTitleTextCurrent
  local tooltipTextCurrent
  local allowAreApproximatelyEqual = false -- Require an exact match when comparing DropDown item text values.
  for i = 1, length do
    valueCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfos[i])
    if (EasyOptions:AreEqual(textCurrent, text, allowAreApproximatelyEqual)) then
      return true, valueCurrent, i
    end
  end
  return false, nil, nil
end
function EasyOptions:ContainsItemWithCustomValueInDropDownItemInfos(dropDownItemInfos, dropDownItemInfosDefault) -- containsItemWithCustomValue, index
  if (dropDownItemInfos == nil) then
    return false, nil
  end

  local length = EasyOptions:GetLength(dropDownItemInfos)
  local valueCurrent
  local textCurrent
  local tooltipTitleTextCurrent
  local tooltipTextCurrent
  local allowAreApproximatelyEqual = false -- Require an exact match when comparing DropDown item values.
  for i = 1, length do
    valueCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfos[i])
    if (not EasyOptions:ContainsValueInDropDownItemInfos(valueCurrent, dropDownItemInfosDefault)) then
      return true, i
    end
  end
  return false, nil
end
function EasyOptions:AddDropDownItem(dropDown, value, text, promptUserIfItemValueExists, promptUserIfItemTextExists, allowShowErrorMessage, allowPrintErrorMessage)
  -- Description: Adds an item with the specified value to dropDown.DropDownItemInfos.
  local uniqueID = "EasyOptions.AddDropDownItem"
  if (text == nil) or (text == "") then
    local message = L("You must specify a name for the drop down item.")
    if (allowPrintErrorMessage) then
      EasyOptions:PrintErrorMessage(message)
    end
    if (allowShowErrorMessage) then
      EasyOptions:ShowDialogOk(message, uniqueID, nil)
    end
    return
  end
  if (EasyOptions:TrimString(text) == "") then
    local message = L("The item name must contain at least one non-white space character.")
    if (allowPrintErrorMessage) then
      EasyOptions:PrintErrorMessage(message)
    end
    if (allowShowErrorMessage) then
      EasyOptions:ShowDialogOk(message, uniqueID, nil)
    end
    return
  end
  if (value == nil) then -- IMPORTANT: DropDowns and DropDownItemInfos do not support a value of nil.
    local message = L("You must specify a value for the drop down item.")
    if (allowPrintErrorMessage) then
      EasyOptions:PrintErrorMessage(message)
    end
    if (allowShowErrorMessage) then
      EasyOptions:ShowDialogOk(message, uniqueID, nil)
    end
    return
  end

  local containsValue, textOfItemWithValue, indexOfValue = dropDown:ContainsValue(value)
  local containsText, valueOfItemWithText, indexOfText = dropDown:ContainsText(text)
  if (containsValue) and (text == textOfItemWithValue) then
    if (promptUserIfItemValueExists) or (promptUserIfItemTextExists) then
      local promptText = L("That item already exists in the drop down.")
      local onAcceptHandler = nil
      EasyOptions:ShowDialogOk(promptText, uniqueID, onAcceptHandler)
    end
    return
  end
  if (containsText) and (promptUserIfItemTextExists) then
    local itemName = EasyOptions:IIF((text ~= nil), EasyOptions:Format("\"{1}\"", text), EasyOptions:Format(text))
    local promptText = L("An item with a name of {1} already exists in the drop down.\n\nDo you still want to use that name for the item? Doing so will cause multiple items in the drop down to have a name of {1}.", itemName)
    local uniqueIDLocal = uniqueID..".DuplicateItemName" -- IMPORTANT: We must use an ID that is unique to this case because EasyOptions:AddDropDownItem can be called recursively from within this case which can result in a second dialog being shown by EasyOptions:AddDropDownItem before a previously shown dialog is registered as being closed by the WoW API.
    local onAcceptHandler = function(userText)
      EasyOptions:AddDropDownItem(dropDown, value, text, promptUserIfItemValueExists, false, allowShowErrorMessage, allowPrintErrorMessage) -- Call this method recursively, but pass false to the promptUserIfItemTextExists parameter.
    end
    local onCancelHandler = nil
    EasyOptions:ShowDialogOkCancel(promptText, uniqueIDLocal, onAcceptHandler, onCancelHandler)
    return
  end
  if (containsValue) and (promptUserIfItemValueExists) then
    local itemName = EasyOptions:IIF((text ~= nil) and (text ~= ""), EasyOptions:Format("\"{1}\"", text), L("the item"))
    local promptText = L("An item with the specified value already exists in the drop down.\n\nDo you want to rename that item to {1}?", itemName)
    local uniqueIDLocal = uniqueID..".DuplicateValue" -- IMPORTANT: We must use an ID that is unique to this case because EasyOptions:AddDropDownItem can be called recursively from within this case which can result in a second dialog being shown by EasyOptions:AddDropDownItem before a previously shown dialog is registered as being closed by the WoW API.
    local onAcceptHandler = function(userText)
      EasyOptions:AddDropDownItem(dropDown, value, text, false, promptUserIfItemTextExists, allowShowErrorMessage, allowPrintErrorMessage) -- Call this method recursively, but pass false to the promptUserIfItemValueExists parameter.
    end
    local onCancelHandler = nil
    EasyOptions:ShowDialogOkCancel(promptText, uniqueIDLocal, onAcceptHandler, onCancelHandler)
    return
  end
  EasyOptions:AddDropDownItem_Internal(dropDown, value, text)
end
function EasyOptions:AddDropDownItem_Internal(dropDown, value, text)
  local length = EasyOptions:GetLength(dropDown.DropDownItemInfos)
  local containsValue, textOfItemWithValue, index = dropDown:ContainsValue(value)
  local dropDownItemInfo = {value, text}
  local dropDownItemInfos
  if (containsValue) then
    dropDownItemInfos = EasyOptions:CopyTable(dropDown.DropDownItemInfos) -- IMPORTANT: We must copy the DropDown's table in order to prevent the modified table from being used when we determine the appropriate DropDown item to select after the item is removed from the DropDown's table.
    table.remove(dropDownItemInfos, index)
    table.insert(dropDownItemInfos, index, dropDownItemInfo)
  elseif (length == 0) then -- NOTE: If dropDown.DropDownItemInfos only contains one item and we are removing that item then we want dropDownItemInfos to be nil.
    dropDownItemInfos = {dropDownItemInfo}
  else
    dropDownItemInfos = EasyOptions:CopyTable(dropDown.DropDownItemInfos) -- IMPORTANT: We must copy the DropDown's table in order to prevent the modified table from being used when we determine the appropriate DropDown item to select after the item is removed from the DropDown's table.
    table.insert(dropDownItemInfos, dropDownItemInfo)
  end

  local defaultValue = value
  local allowSetToDefaultValue = true
  dropDown:SetDropDownItemInfos(dropDownItemInfos, defaultValue, allowSetToDefaultValue)
  if (dropDown:GetValue() ~= value) then
    dropDown:SetValue(value)
  end
end
function EasyOptions:RemoveDropDownItem(dropDown, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  -- Description: Removes the item with the specified value from dropDown.DropDownItemInfos.
  if (value == nil) then -- IMPORTANT: DropDowns and DropDownItemInfos do not support a value of nil.
    return false
  end

  local containsValue, textOfItemWithValue, index = dropDown:ContainsValue(value)
  if (not containsValue) then
    local itemName = EasyOptions:IIF((text ~= nil) and (text ~= ""), EasyOptions:Format("\"{1}\"", text), L("the item"))
    local message = L("Unable to delete {1} from the drop down because the drop down does not contain that item.", itemName)
    if (allowPrintErrorMessage) then
      EasyOptions:PrintErrorMessage(message)
    end
    if (allowShowErrorMessage) then
      local uniqueID = "EasyOptions.RemoveDropDownItem"
      EasyOptions:ShowDialogOk(message, uniqueID, nil)
    end
    return false
  end
  
  local length = EasyOptions:GetLength(dropDown.DropDownItemInfos)
  local dropDownItemInfos
  if (length <= 1) then -- NOTE: If dropDown.DropDownItemInfos only contains one item and we are removing that item then we want dropDownItemInfos to be nil.
    dropDownItemInfos = nil
  else
    dropDownItemInfos = EasyOptions:CopyTable(dropDown.DropDownItemInfos) -- IMPORTANT: We must copy the DropDown's table in order to prevent the modified table from being used when we determine the appropriate DropDown item to select after the item is removed from the DropDown's table.
    table.remove(dropDownItemInfos, index)
  end
  local valueToSelect, indexToSelect = dropDown:GetValueToSelectAfterRemoveDropDownItem(dropDownItemInfos)

  local defaultValue = valueToSelect
  local allowSetToDefaultValue = true
  dropDown:SetDropDownItemInfos(dropDownItemInfos, defaultValue, allowSetToDefaultValue)
  return true
end
function EasyOptions:CanMoveDropDownItemToIndex(dropDown, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
  -- Description: Indicates whether the item with the specified value can be moved to the specified index.
  if (value == nil) then
    return false, nil, nil
  end
  if (indexDesired == nil) then
    return false, nil, nil
  end
  
  local containsValue, textOfItemWithValue, index = dropDown:ContainsValue(value)
  if (not containsValue) then
    local itemName = EasyOptions:IIF((text ~= nil) and (text ~= ""), EasyOptions:Format("\"{1}\"", text), L("the item"))
    local message = L("Unable to move {1} in the drop down list because the drop down does not contain that item.", itemName)
    if (allowPrintErrorMessage) then
      EasyOptions:PrintErrorMessage(message)
    end
    if (allowShowErrorMessage) then
      local uniqueID = "EasyOptions.CanMoveDropDownItemToIndex"
      EasyOptions:ShowDialogOk(message, uniqueID, nil)
    end
    return false, nil, nil
  end
  
  local length = EasyOptions:GetLength(dropDown.DropDownItemInfos)
  local indexDesiredLimited = EasyOptions:LimitValue(indexDesired, 1, length, false)
  if (index == indexDesiredLimited) then
    return false, nil, nil
  end

  return true, index, indexDesiredLimited
end
function EasyOptions:MoveDropDownItem(dropDown, value, text, indexDesired, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  -- Description: Moves the item with the specified value to the specified index in dropDown.DropDownItemInfos.
  local canMoveDropDownItem, indexCurrent, indexDesiredLimited = EasyOptions:CanMoveDropDownItemToIndex(dropDown, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage)
  if (not canMoveDropDownItem) then
    return false
  end

  local dropDownItemInfos = EasyOptions:CopyTable(dropDown.DropDownItemInfos)
  local item = dropDownItemInfos[indexCurrent]
  table.remove(dropDownItemInfos, indexCurrent)
  table.insert(dropDownItemInfos, indexDesiredLimited, item)

  local defaultValue = value
  local allowSetToDefaultValue = true
  dropDown:SetDropDownItemInfos(dropDownItemInfos, defaultValue, allowSetToDefaultValue)

  if (printSuccessMessage) then
    local itemName = EasyOptions:IIF((text ~= nil) and (text ~= ""), EasyOptions:Format("\"{1}\"", text), L("The drop down item"))
    local length = EasyOptions:GetLength(dropDown.DropDownItemInfos)
    EasyOptions:PrintMessage(L("{1} was moved to index {2} of {3}.", itemName, indexDesiredLimited, length))
  end

  return true
end
function EasyOptions:CanMoveDropDownItemUp(dropDown, value, text, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
  -- Description: Indicates whether the index of the item with the specified value can be decreased by 1 in dropDown.DropDownItemInfos.
  local containsValue, textOfItemWithValue, index = dropDown:ContainsValue(value)
  local indexDesired
  if (not containsValue) then
    indexDesired = nil
  else
    indexDesired = index - 1
  end
  return EasyOptions:CanMoveDropDownItemToIndex(dropDown, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage)
end
function EasyOptions:MoveDropDownItemUp(dropDown, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  -- Description: Decreases the index of the item with the specified value by 1 in dropDown.DropDownItemInfos.
  local canMoveDropDownItem, indexCurrent, indexDesiredLimited = EasyOptions:CanMoveDropDownItemUp(dropDown, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  if (not canMoveDropDownItem) then
    return false
  end
  return EasyOptions:MoveDropDownItem(dropDown, value, text, indexDesiredLimited, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
end
function EasyOptions:CanMoveDropDownItemDown(dropDown, value, text, allowShowErrorMessage, allowPrintErrorMessage) -- canMoveDropDownItem, indexCurrent, indexDesiredLimited
  -- Description: Indicates whether the index of the item with the specified value can be increased by 1 in dropDown.DropDownItemInfos.
  local containsValue, textOfItemWithValue, index = dropDown:ContainsValue(value)
  local indexDesired
  if (not containsValue) then
    indexDesired = nil
  else
    indexDesired = index + 1
  end
  return EasyOptions:CanMoveDropDownItemToIndex(dropDown, value, text, indexDesired, allowShowErrorMessage, allowPrintErrorMessage)
end
function EasyOptions:MoveDropDownItemDown(dropDown, value, text, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
  -- Description: Increases the index of the item with the specified value by 1 in dropDown.DropDownItemInfos.
  local canMoveDropDownItem, indexCurrent, indexDesiredLimited = EasyOptions:CanMoveDropDownItemDown(dropDown, value, text, allowShowErrorMessage, allowPrintErrorMessage)
  if (not canMoveDropDownItem) then
    return false
  end
  return EasyOptions:MoveDropDownItem(dropDown, value, text, indexDesiredLimited, printSuccessMessage, allowShowErrorMessage, allowPrintErrorMessage)
end
function EasyOptions:GetDefaultDropDownItems(dropDown, copyTable)
  if (dropDown == nil) then
    return nil
  end

  local optionItem = dropDown.OptionItem
  if (optionItem == nil) then
    return nil
  end

  local result
  if (not copyTable) then
    result = optionItem.DropDownItemInfos
  else
    result = EasyOptions:CopyTable(optionItem.DropDownItemInfos)
  end
  return result
end
function EasyOptions:GetValueToSelectAfterRemoveDropDownItem(dropDown, dropDownItemInfosDesired) -- value, index
  if (dropDown == nil) then
    return nil, nil
  end

  local selectedValue = dropDown:GetValue()
  local containsValue, index = EasyOptions:ContainsValueInDropDownItemInfos(selectedValue, dropDownItemInfosDesired)
  if (containsValue) then
    return selectedValue, index
  end
  
  local length = EasyOptions:GetLength(dropDownItemInfosDesired)
  local indexOfSelectedValue = dropDown:GetIndexOfSelectedValue() or 1 -- Select the first item in the list by default if no items are selected.
  local index
  local value
  if (length == 0) then
    index = nil
    value = nil
  else
    local index = math.min(indexOfSelectedValue, length) -- Select the item in the new list that corresponds to the index of the previously selected item.
    local textLocal -- not used.
    local tooltipTitleTextLocal -- not used.
    local tooltipTextLocal -- not used.
    value, textLocal, tooltipTitleTextLocal, tooltipTextLocal = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfosDesired[index])
  end
  return value, index
end
function EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfo) -- value, text, tooltipTitleText, tooltipText
  if (dropDownItemInfo == nil) then
    return nil, "", nil, nil
  elseif (type(dropDownItemInfo) == "number") then -- e.g., 1
    return dropDownItemInfo, tostring(dropDownItemInfo), nil, nil
  elseif (type(dropDownItemInfo) == "string") then -- e.g., "Value 1"
    return dropDownItemInfo, dropDownItemInfo, nil, nil
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 1) and (type(dropDownItemInfo[1]) == "number") then -- e.g., {"Value 1"}
    return dropDownItemInfo[1], tostring(dropDownItemInfo[1]), nil, nil
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 1) and (type(dropDownItemInfo[1]) == "string") then -- e.g., {"Value 1"}
    return dropDownItemInfo[1], dropDownItemInfo[1], nil, nil
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 2) and (type(dropDownItemInfo[1]) == "number") and (type(dropDownItemInfo[2]) == "string") then -- e.g., {1, "Value 1"}
    return dropDownItemInfo[1], dropDownItemInfo[2], nil, nil
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 2) and (type(dropDownItemInfo[1]) == "string") and (type(dropDownItemInfo[2]) == "string") then -- e.g., {"One", "Value 1"}
    return dropDownItemInfo[1], dropDownItemInfo[2], nil, nil
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 3) and (type(dropDownItemInfo[1]) == "number") and (type(dropDownItemInfo[2]) == "string") and (type(dropDownItemInfo[3]) == "string") then -- e.g., {1, "Value 1"}
    return dropDownItemInfo[1], dropDownItemInfo[2], dropDownItemInfo[2], dropDownItemInfo[3]
  elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 3) and (type(dropDownItemInfo[1]) == "string") and (type(dropDownItemInfo[2]) == "string") and (type(dropDownItemInfo[3]) == "string") then -- e.g., {"One", "Value 1"}
    return dropDownItemInfo[1], dropDownItemInfo[2], dropDownItemInfo[2], dropDownItemInfo[3]
  else
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for its DropDown item information. DropDown item information must be in one of the following formats: number, string, {number}, {string}, {number, string}, or {string, string}."))
    return nil, "", nil, nil
  end
end
function EasyOptions:GetDefaultDropDownWidth()
  return 140
end
function EasyOptions:GetDropDownButtonWidth()
  return 19
end
function EasyOptions:GetDropDownWidthEffective(width)
  local dropDownButtonWidth = EasyOptions:GetDropDownButtonWidth()
  return math.max(width + (dropDownButtonWidth - 21), 0)
end

function EasyOptions:CreateOptionEditableDropDownBase(parent, positionInfo, optionItem, textDefault, tooltipTextDefault_ValueDropDown, tooltipTextDefault_ValueTextBox, textDefault_TestButton, tooltipTextDefault_TestButton, dialogTextBoxInfo)
  local optionName = optionItem.Name
  local value = EasyOptions:GetOptionValueOrDefaultValue(optionName, optionItem.DefaultValue)
  local text = EasyOptions:GetControlText(optionItem)
  local tooltipTitleText = optionItem.TooltipTitleText or text
  local tooltipText = optionItem.TooltipText
  local showTextBox = optionItem.ShowTextBox
  local onOptionValueChangedHandlerCustom = optionItem.OnOptionValueChangedHandler
  optionItem.OnOptionValueChangedHandler = function(self)
    local optionItem = self
    local control = optionItem.Control
    local value = optionItem:GetOptionValue()
    control:SetValue(value)
    if (onOptionValueChangedHandlerCustom ~= nil) then
      onOptionValueChangedHandlerCustom(self)
    end
  end
  
  EasyOptions:ValidateEditableDropDownOptionItem(optionItem)

  local controlType = optionItem.ControlType
  local hasTestButton = (optionItem.TestButtonText ~= nil) or (optionItem.TestButtonTooltipText ~= nil) or (optionItem.OnTestHandler ~= nil)
  local dialogTextBoxInfoLocal = dialogTextBoxInfo

  local controlTypeName = controlType
  local customControlName = nil
  local controlName = EasyOptions:GetControlName(controlTypeName, customControlName)
  local control = CreateFrame("Frame", controlName, parent) -- Acts as the control that is associated with the OptionItem.
  control.Name = controlName
  control.Type = controlTypeName
  control.BaseType = "EditableDropDown"
  -- EasyOptions:SetTooltip(control, control, tooltipTitleText, tooltipText)
  control:EnableMouse(false)
  
  control.OnEnabledChangedHandler = function(self)
    -- Reserved.
  end
  EasyOptions:AddSupportForEnabledState(control)

  control:ClearAllPoints()
  control:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3], positionInfo[4])
  
  optionItem.Control = control
  control.OptionItem = optionItem
  control.HasTestButton = hasTestButton
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  local dropDownItemInfos = EasyOptions:GetEditableDropDownItemInfosFromAddonDB(control) -- IMPORTANT: We must not call EasyOptions:GetEditableDropDownItemInfosFromAddonDB until after control.OptionItem is set.
  
  control.GetValue = function(self)
    return self.ValueDropDown:GetValue()
    -- return self.ValueTextBox:GetText()
  end
  control.SetValue = function(self, value)
    EasyOptions:SetEditableDropDownValue(self, value)
  end
  control.EnableHandleControlValueChanged = true
  control.HandleControlValueChanged = function(self, childControl)
    local control = self
    local optionItem = control.OptionItem
    if (not control.EnableHandleControlValueChanged) then
      return
    end
    local value
    if (childControl == control.ValueDropDown) then
      value = control.ValueDropDown:GetValue()
    elseif (control.ValueTextBox ~= nil) and (childControl == control.ValueTextBox) then
      value = control.ValueTextBox:GetText()
      if (value == "") then
        value = nil
      end
    else
      value = optionItem:GetOptionValue() or optionItem.DefaultValue
      EasyOptions:PrintErrorMessageOnce(L("Unknown child control in HandleControlValueChanged."))
    end
    control:SetValue(value)
  end
  
  -- Create the controls.
  local controlNameCurrent
  local textCurrent
  local tooltipTitleTextCurrent
  local tooltipTextCurrent
  local messageSuffix
  local positionInfoCurrent = {"TOPLEFT", "TOPLEFT", 0, 0} -- init -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local positionInfoTemp1
  local positionInfoTemp2
  local paddingX = EasyOptions:GetDefaultControlPaddingX()
  local paddingX_ToolButton = 0.0 * EasyOptions:GetDefaultControlPaddingX()
  local paddingX_TestButton = 0.0 * EasyOptions:GetDefaultControlPaddingX()
  local paddingY = 0.5 * EasyOptions:GetDefaultControlPaddingY()
  local customSize_DropDown = optionItem.Width -- width or {width, height}
  local customSize_ToolButton = 30
  local customSize_HalfHeightButtonHeight = math.max(0.5 * EasyOptions:GetDefaultButtonHeight() - 1, 0) -- Modify the height to help the controls align better with the other controls.
  local customSize_HalfHeightButtonWidth = {customSize_ToolButton, customSize_HalfHeightButtonHeight}
  local customSize_TestButton = 57.5 -- This width will cause the right edge of the TestButton to align with the right edge of the ValueTextBox when the default widths are used. This width also allows enough room for the word "Test" after it is translated to all supported languages.
  local customSize_TextBox = optionItem.TextBoxWidth or nil -- "Fill"
  local offsetY_HalfHeightButton_Top = 1 -- Offset the controls to help them align better with the other controls.
  local offsetY_HalfHeightButton_Bottom = customSize_HalfHeightButtonHeight + offsetY_HalfHeightButton_Top
  local controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent
  local controls = {}
  local controlRegionWidth = 0 -- init
  local controlRegionHeight = 0 -- init
  -- local sign = EasyOptions:IIF(EasyOptions:IsRightAlignedPositionInfo(positionInfo), -1, 1)
  local text_Value = EasyOptions:IIF(text == "", nil, text) or textDefault -- NOTE: This is used by multiple controls in this method.
  local tooltipTitleText_Value = optionItem.TooltipTitleText or text_Value
  --
  -- ValueDropDown
  textCurrent = text_Value
  tooltipTitleTextCurrent = tooltipTitleText_Value
  tooltipTextCurrent = tooltipText or tooltipTextDefault_ValueDropDown or EasyOptions:GetDefaultEditableDropDownTooltipText()
  controlNameCurrent = controlName..".ValueDropDown"
  positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], 0, 0}
  local onClickHandler_ValueDropDown = function(self, info, arg1, arg2, checked)
    local control = self.Owner.Parent -- NOTE: self is the DropDown, self.Owner is the DropDownParent, self.Owner.Parent is the frame that acts as the parent control.
    local optionItem = control.OptionItem
    if (optionItem.OnClickHandler ~= nil) then
      optionItem.OnClickHandler(self, info, arg1, arg2, checked)
    end
  end
  local onValueChangedHandler_ValueDropDown = function(self, value)
    local control = self.Owner.Parent -- NOTE: self is the DropDown, self.Owner is the DropDownParent, self.Owner.Parent is the frame that acts as the parent control.
    local optionItem = control.OptionItem
    local valueLocal = control.ValueDropDown:GetValue() -- IMPORTANT: We must not use the value of the value parameter because the value of that parameter always seems to be an empty table.
    if (optionItem.OnValueChangedHandler ~= nil) then
      optionItem.OnValueChangedHandler(self, value)
    end
    if (valueLocal == optionItem:GetOptionValue()) then
      return
    end
    control:HandleControlValueChanged(control.ValueDropDown)
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateDropDown(control, controlNameCurrent, value, dropDownItemInfos, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_DropDown, nil, nil, onClickHandler_ValueDropDown, onValueChangedHandler_ValueDropDown)
  controlRegionWidthCurrent = EasyOptions:GetDropDownWidthEffective(customSize_DropDown or EasyOptions:GetDefaultDropDownWidth()) -- Set controlRegionWidthCurrent to the width of the DropDown because we want to ignore the width of the label if the label is wider than the DropDown.
  controlCurrent.OptionItem = optionItem
  controlCurrent.DropDown.OptionItem = optionItem
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  controlCurrent.DropDown.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  controlCurrent.IsEditableDropDown = true
  controlCurrent.DropDown.IsEditableDropDown = true
  EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent.DropDown)
  control.ValueDropDown = controlCurrent
  controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  local offsetY_Button_Row1 = math.max(17 - (42 - controlRegionHeight), 0)
  --
  -- AddItemButton
  textCurrent = nil -- "+"
  tooltipTitleTextCurrent = L("Add or View Item")
  tooltipTextCurrent = L("Add a new item to the drop down list or view the value of the selected item in the drop down list.\n\nThis can also be used to rename an existing item by specifying a new name for a value that already exists in the drop down list.")
  controlNameCurrent = controlName..".AddItemButton"
  positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + controlRegionWidthCurrent + paddingX_ToolButton, positionInfoCurrent[4] - offsetY_Button_Row1} -- Correct for offsetY_Button_Row1.
  local onClickHandler_AddItemButton = function(self, button)
    local control = self.Parent
    local dropDown = control.ValueDropDown.DropDown
    local optionItem = control.OptionItem
    local defaultItemName = L("Item").." "..tostring(dropDown:GetNumberOfDropDownItems() + 1) -- e.g., "Item 4" if the DropDown currently has 3 items.
    local text = dropDown:GetText()
    local value = dropDown:GetValue()
    if (text == nil) or (text == "") then
      text = defaultItemName
    end
    if (value == nil) or (value == "") then
      if (optionItem.DefaultValue ~= nil) then
        value = EasyOptions:Format(optionItem.DefaultValue)
      else
        value = ""
      end
    end
    local textBoxInfo = {
      TextBoxCount = 2, -- 0, 1, or 2
      TextBox1LabelText = L("Item Name"), 
      TextBox1Text = text, 
      TextBox1Tooltip = L("Enter the display name for the drop down item."), 
      TextBox1Width = nil, -- Use the default value.
      TextBox2LabelText = L("Value"), 
      TextBox2Text = value, 
      TextBox2Tooltip = L("Enter the value to associate with the drop down item."), 
      TextBox2Width = nil, -- Use the default value.
    }
    if (dialogTextBoxInfoLocal ~= nil) then -- Merge dialogTextBoxInfoLocal with textBoxInfo (if required).
      textBoxInfo.TextBoxCount = dialogTextBoxInfoLocal.TextBoxCount or textBoxInfo.TextBoxCount -- 0, 1, or 2
      textBoxInfo.TextBox1LabelText = dialogTextBoxInfoLocal.TextBox1LabelText or textBoxInfo.TextBox1LabelText
      -- textBoxInfo.TextBox1Text = textBoxInfo.TextBox1Text
      textBoxInfo.TextBox1Tooltip = dialogTextBoxInfoLocal.TextBox1Tooltip or textBoxInfo.TextBox1Tooltip
      textBoxInfo.TextBox1Width = dialogTextBoxInfoLocal.TextBox1Width
      textBoxInfo.TextBox2LabelText = dialogTextBoxInfoLocal.TextBox2LabelText or textBoxInfo.TextBox2LabelText
      -- textBoxInfo.TextBox2Text = textBoxInfo.TextBox2Text
      textBoxInfo.TextBox2Tooltip = dialogTextBoxInfoLocal.TextBox2Tooltip or textBoxInfo.TextBox2Tooltip
      textBoxInfo.TextBox2Width = dialogTextBoxInfoLocal.TextBox2Width
    end
    local uniqueID = "EasyOptions.EditableDropDown.AddItemButton"
    local onAcceptHandler = function(userText)
      local itemName = userText[1]
      local value = userText[2]
      if (value == "") then
        value = nil
      end
      dropDown:AddDropDownItem(value, itemName, true, true, true, false)
    end
    local onCancelHandler = function() 
      -- Reserved.
    end
    EasyOptions:ShowDialogEditAdvancedOkCancel(textBoxInfo, uniqueID, onAcceptHandler, onCancelHandler)
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_ToolButton, onClickHandler_AddItemButton)
  controlCurrent.OptionItem = optionItem
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  EasyOptions:SetButtonIconTexture(controlCurrent, 6.5, "Interface\\AddOns\\EasyOptions\\Textures\\AddItem.png")
  -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  control.AddItemButton = controlCurrent
  positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 -- Undo the correction for offsetY_Button_Row1.
  controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  -- MoveItemUpButton
  textCurrent = nil -- "-"
  tooltipTitleTextCurrent = L("Move Item Up")
  tooltipTextCurrent = L("Move the selected item up one position in the drop down list.")
  controlNameCurrent = controlName..".MoveItemUpButton"
  positionInfoTemp1 = EasyOptions:CopyTable(positionInfoCurrent) -- Store the current positionInfoCurrent.
  positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + controlRegionWidthCurrent + paddingX_ToolButton, positionInfoCurrent[4] - offsetY_Button_Row1 - offsetY_HalfHeightButton_Top} -- Correct for offsetY_Button_Row1 and offsetY_HalfHeightButton_Top.
  local onClickHandler_MoveItemUpButton = function(self, button)
    local control = self.Parent
    local button = self
    local dropDown = control.ValueDropDown.DropDown
    local allowUpdateTooltip = (EasyOptions.Fields.EnableRefreshTooltipAfterControlValueChanged) and (EasyOptions:IsTooltipOwner(button))
    local printSuccessMessage = not allowUpdateTooltip -- Only print a message if the tooltip will not be updated.
    dropDown:MoveSelectedDropDownItemUp(printSuccessMessage, false, true)
    if (allowUpdateTooltip) then
      button:OnEnterTooltipFunction()
    end
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_HalfHeightButtonWidth, onClickHandler_MoveItemUpButton)
  controlCurrent.OptionItem = optionItem
  controlCurrent.TooltipInfo.TextBase = controlCurrent.TooltipInfo.Text
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  EasyOptions:SetButtonIconTexture(controlCurrent, 1, "Interface\\AddOns\\EasyOptions\\Textures\\ArrowUp.png")
  -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  control.MoveItemUpButton = controlCurrent
  positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 + offsetY_HalfHeightButton_Top -- Undo the correction for offsetY_Button_Row1 and offsetY_HalfHeightButton_Top.
  controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  -- MoveItemDownButton
  textCurrent = nil -- "-"
  tooltipTitleTextCurrent = L("Move Item Down")
  tooltipTextCurrent = L("Move the selected item down one position in the drop down list.")
  controlNameCurrent = controlName..".MoveItemDownButton"
  positionInfoTemp2 = EasyOptions:CopyTable(positionInfoCurrent) -- Store the current positionInfoCurrent.
  positionInfoCurrent = {positionInfoTemp1[1], positionInfoTemp1[2], positionInfoTemp1[3] + controlRegionWidthCurrent + paddingX_ToolButton, positionInfoTemp1[4] - offsetY_Button_Row1 - offsetY_HalfHeightButton_Bottom} -- Correct for offsetY_Button_Row1 and offsetY_HalfHeightButton_Bottom.
  local onClickHandler_MoveItemDownButton = function(self, button)
    local control = self.Parent
    local button = self
    local dropDown = control.ValueDropDown.DropDown
    local allowUpdateTooltip = (EasyOptions.Fields.EnableRefreshTooltipAfterControlValueChanged) and (EasyOptions:IsTooltipOwner(button))
    local printSuccessMessage = not allowUpdateTooltip -- Only print a message if the tooltip will not be updated.
    dropDown:MoveSelectedDropDownItemDown(printSuccessMessage, false, true)
    if (allowUpdateTooltip) then
      button:OnEnterTooltipFunction()
    end
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_HalfHeightButtonWidth, onClickHandler_MoveItemDownButton)
  controlCurrent.OptionItem = optionItem
  controlCurrent.TooltipInfo.TextBase = controlCurrent.TooltipInfo.Text
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  EasyOptions:SetButtonIconTexture(controlCurrent, 1, "Interface\\AddOns\\EasyOptions\\Textures\\ArrowDown.png")
  -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  control.MoveItemDownButton = controlCurrent
  positionInfoCurrent = positionInfoTemp2 -- Restore the previous positionInfoCurrent.
  -- positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 + offsetY_HalfHeightButton_Bottom -- Undo the correction for offsetY_Button_Row1 and offsetY_HalfHeightButton_Bottom.
  -- controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  -- controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  -- RemoveItemButton
  textCurrent = nil -- "X"
  tooltipTitleTextCurrent = L("Remove Item")
  tooltipTextCurrent = L("Remove the selected item from the drop down list.")
  controlNameCurrent = controlName..".RemoveItemButton"
  positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + controlRegionWidthCurrent + paddingX_ToolButton, positionInfoCurrent[4] - offsetY_Button_Row1} -- Correct for offsetY_Button_Row1.
  local onClickHandler_RemoveItemButton = function(self, button)
    local control = self.Parent
    local dropDown = control.ValueDropDown.DropDown
    local dropDownValue = dropDown:GetValue()
    local dropDownText = dropDown:GetText()
    local uniqueID = "EasyOptions.EditableDropDown.RemoveItemButton"
    local onAcceptHandler = function(userText) 
      dropDown:RemoveDropDownItem(dropDownValue, dropDownText, false, true) -- IMPORTANT: We use dropDown:RemoveDropDownItem rather than dropDown:RemoveSelectedDropDownItem in order to ensure that we remove the item that was shown in the dialog window even if the user selects a different item in the DropDown after the dialog was shown.
    end
    local onCancelHandler = function() 
      -- Reserved.
    end
    EasyOptions:ShowDialogOkCancel(L("Remove \"{1}\"", dropDownText), uniqueID, onAcceptHandler, onCancelHandler)
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_ToolButton, onClickHandler_RemoveItemButton)
  controlCurrent.OptionItem = optionItem
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  EasyOptions:SetButtonIconTexture(controlCurrent, 4.5, "Interface\\AddOns\\EasyOptions\\Textures\\Delete.png")
  -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  control.RemoveItemButton = controlCurrent
  positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 -- Undo the correction for offsetY_Button_Row1.
  controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  -- RestoreDefaultItemsButton
  textCurrent = nil -- "O"
  tooltipTitleTextCurrent = L("Restore Default Items")
  messageSuffix = L("\n\nThis will restore the item names of default items that were renamed and the order of default items that were moved.\n\nYou will be prompted before items with custom values are removed.")
  tooltipTextCurrent = L("Restore the drop down's default items.{1}", messageSuffix)
  controlNameCurrent = controlName..".RestoreDefaultItemsButton"
  positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + controlRegionWidthCurrent + paddingX_ToolButton, positionInfoCurrent[4] - offsetY_Button_Row1} -- Correct for offsetY_Button_Row1.
  local onClickHandler_RestoreDefaultItemsButton = function(self, button)
    local control = self.Parent
    local dropDown = control.ValueDropDown.DropDown
    local uniqueID = "EasyOptions.EditableDropDown.RestoreDefaultItemsButton"
    local onAcceptHandler = function(userText) 
      EasyOptions:RestoreDefaultEditableDropDownItems(dropDown, true)
    end
    local onCancelHandler = function() 
      -- Reserved.
    end
    EasyOptions:ShowDialogOkCancel(L("Restore Default Items{1}", messageSuffix), uniqueID, onAcceptHandler, onCancelHandler)
  end
  controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_ToolButton, onClickHandler_RestoreDefaultItemsButton)
  controlCurrent.OptionItem = optionItem
  controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  EasyOptions:SetButtonIconTexture(controlCurrent, 2, "Interface\\AddOns\\EasyOptions\\Textures\\RestoreDefaults.png")
  -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
  control.RestoreDefaultItemsButton = controlCurrent
  positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 -- Undo the correction for offsetY_Button_Row1.
  controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
  controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
  table.insert(controls, controlCurrent)
  --
  -- TestButton
  if (hasTestButton) then
    textCurrent = optionItem.TestButtonText or textDefault_TestButton or L("Test")
    tooltipTitleTextCurrent = textCurrent
    tooltipTextCurrent = optionItem.TestButtonTooltipText or tooltipTextDefault_TestButton or L("Click to test using the selected value.")
    controlNameCurrent = controlName..".TestButton"
    positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + controlRegionWidthCurrent + paddingX_TestButton, positionInfoCurrent[4] - offsetY_Button_Row1} -- Correct for offsetY_Button_Row1.
    local onClickHandler_TestButton = function(self, button)
      local control = self.Parent
      local optionItem = control.OptionItem
      local value = optionItem:GetOptionValue()
      if (optionItem.OnTestHandler ~= nil) then
        optionItem:OnTestHandler(value)
      end
    end
    controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateButton(control, controlNameCurrent, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_TestButton, onClickHandler_TestButton)
    controlCurrent.OptionItem = optionItem
    controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
    -- EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
    control.TestButton = controlCurrent
    positionInfoCurrent[4] = positionInfoCurrent[4] + offsetY_Button_Row1 -- Undo the correction for offsetY_Button_Row1.
    controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
    controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
    table.insert(controls, controlCurrent)
  end
  --
  -- ValueTextBox
  if (showTextBox) then
    textCurrent = nil -- text_Value
    tooltipTitleTextCurrent = tooltipTitleText_Value
    tooltipTextCurrent = optionItem.TextBoxTooltipText or tooltipTextDefault_ValueTextBox or L("Specifies the value that is associated with this item.\n\nYou can select an item from the drop down or enter a custom value.\n\nSelecting an item from the drop down will update this control with the value of the selected item.")
    controlNameCurrent = controlName..".ValueTextBox"
    positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], 0, positionInfoCurrent[4] - controlRegionHeight - paddingY}
    -- customSize_TextBox = customSize_TextBox or (controlRegionWidth - 7.5)
    local onTextAcceptedHandler_ValueTextBox = function(self)
      local control = self.Parent
      local optionItem = control.OptionItem
      local value = control.ValueTextBox:GetText()
      if (value == optionItem:GetOptionValue()) then
        return
      end
      control:HandleControlValueChanged(control.ValueTextBox)
    end
    controlCurrent, controlRegionWidthCurrent, controlRegionHeightCurrent = EasyOptions:CreateTextBox(control, controlNameCurrent, value, nil, nil, nil, textCurrent, tooltipTitleTextCurrent, tooltipTextCurrent, positionInfoCurrent, customSize_TextBox, false, false, nil, nil, nil, nil, onTextAcceptedHandler_ValueTextBox, nil)
    controlCurrent.OptionItem = optionItem
    controlCurrent.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
    EasyOptions:AddRightButtonDownHandlerForOptionControl(controlCurrent)
    control.ValueTextBox = controlCurrent
    control.AllowEnableValueTextBox = true
    controlRegionWidth = math.max(controlRegionWidthCurrent + positionInfoCurrent[3], controlRegionWidth)
    controlRegionHeight = math.max(controlRegionHeightCurrent - positionInfoCurrent[4], controlRegionHeight)
    table.insert(controls, controlCurrent)
  end
  
  EasyOptions:AddControlsToOwner(controls, control)
  EasyOptions:AddControlsToParent(controls, control)
  EasyOptions:SetIsSubOptionItemControl(controls)
  
  control.IsInitialized = true
  EasyOptions:UpdateEditableDropDownControlStates(control)

  local customSize = EasyOptions:IIF(customSize_TextBox == "Fill", "Fill", nil)
  local defaultWidth = controlRegionWidth
  local defaultHeight = controlRegionHeight
  local widthMaxAllowed = EasyOptions.Fields.ControlSizeMaxAllowed_Large
  local ignoreCustomHeight = false
  local width, height, hasWidth, hasHeight, shouldFillWidth = EasyOptions:GetSizeFromCustomSize(customSize, ignoreCustomHeight, defaultWidth, defaultHeight, widthMaxAllowed, EasyOptions.Fields.ControlSizeMaxAllowed_Large, true, false)
  controlRegionWidth = width
  controlRegionHeight = height
  control:SetSize(controlRegionWidth, controlRegionHeight)
  if (shouldFillWidth) then
    EasyOptions:SetControlPointToFillWidth(control, parent, positionInfo, 0, 0)
  end
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionEditableDropDown(parent, positionInfo, optionItem)
  local textDefault = nil
  local tooltipTextDefault_ValueDropDown = nil
  local tooltipTextDefault_ValueTextBox = nil
  local textDefault_TestButton = nil
  local tooltipTextDefault_TestButton = nil
  local dialogTextBoxInfo = nil -- Use the default parameter values.
  return EasyOptions:CreateOptionEditableDropDownBase(parent, positionInfo, optionItem, textDefault, tooltipTextDefault_ValueDropDown, tooltipTextDefault_ValueTextBox, textDefault_TestButton, tooltipTextDefault_TestButton, dialogTextBoxInfo)
end
function EasyOptions:ValidateEditableDropDownOptionItem(optionItem)
  local defaultValue = optionItem.DefaultValue
  local dropDownItemInfos = optionItem.DropDownItemInfos
  if (type(defaultValue) ~= "string") then
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for its DefaultValue. The DefaultValue for an EditableDropDown must be of type string."))
  end

  local length = EasyOptions:GetLength(dropDownItemInfos)
  local dropDownItemInfo
  for i = 1, length do
    dropDownItemInfo = dropDownItemInfos[i]
    if (type(dropDownItemInfo) == "string") then -- e.g., "Value 1"
      -- Do nothing.
    elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 1) and (type(dropDownItemInfo[1]) == "string") then -- e.g., {"Value 1"}
      -- Do nothing.
    elseif (type(dropDownItemInfo) == "table") and (EasyOptions:GetLength(dropDownItemInfo) == 2) and (type(dropDownItemInfo[1]) == "string") and (type(dropDownItemInfo[2]) == "string") then -- e.g., {"One", "Value 1"}
      -- Do nothing.
    else
      EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for its DropDown item information. DropDown item information for an EditableDropDown must be in one of the following formats: string, {string}, or {string, string}."))
      break
    end
  end
end
function EasyOptions:UpdateEditableDropDownControlStates(control)
  if (control == nil) or (not control.IsInitialized) then
    return
  end

  local optionItem = control.OptionItem
  local dropDown = control.ValueDropDown.DropDown
  local containsValue, textOfItemWithValue, index = dropDown:ContainsSelectedValue()
  local canStoreInAddonDB, addonDB, optionName = EasyOptions:CanStoreEditableDropDownItemInfosInAddonDB(optionItem)
  local numberOfDropDownItems = dropDown:GetNumberOfDropDownItems()

  local optionValue = optionItem:GetOptionValue()
  -- if (EasyOptions.Current.IsBuildingOptionFrame) then
  --   optionValue = nil
  -- else
  --   optionValue = optionItem:GetOptionValue()
  -- end
  
  control.ValueDropDown:SetEnabled(numberOfDropDownItems >= 1)
  control.AddItemButton:SetEnabled(canStoreInAddonDB)
  control.MoveItemUpButton:SetEnabled(canStoreInAddonDB and dropDown:CanMoveSelectedDropDownItemUp(false, false))
  control.MoveItemDownButton:SetEnabled(canStoreInAddonDB and dropDown:CanMoveSelectedDropDownItemDown(false, false))
  control.RemoveItemButton:SetEnabled(canStoreInAddonDB and containsValue)
  control.RestoreDefaultItemsButton:SetEnabled(canStoreInAddonDB and EasyOptions:ShouldAllowRestoreDefaultEditableDropDownItems(dropDown))
  if (control.TestButton ~= nil) then
    control.TestButton:SetEnabled((optionValue ~= nil) and (optionValue ~= EasyOptions.Fields.EmptyDropDownValue))
    -- control.TestButton:SetEnabled(((optionValue ~= nil) and (optionValue ~= EasyOptions.Fields.EmptyDropDownValue)) or (EasyOptions.Current.IsBuildingOptionFrame))
  end
  if (control.ValueTextBox ~= nil) then
    control.ValueTextBox:SetEnabled(canStoreInAddonDB and control.AllowEnableValueTextBox)
  end

  EasyOptions:UpdateMoveItemUpDownButtonTooltipText(control.MoveItemUpButton, index, numberOfDropDownItems)
  EasyOptions:UpdateMoveItemUpDownButtonTooltipText(control.MoveItemDownButton, index, numberOfDropDownItems)
end
function EasyOptions:UpdateMoveItemUpDownButtonTooltipText(control, index, numberOfDropDownItems)
  if (control == nil) then
    return
  end
  if (control.TooltipInfo == nil) then
    return
  end

  local text = control.TooltipInfo.TextBase
  if (index ~= nil) and (index >= 1) and (numberOfDropDownItems >= 1) and (EasyOptions.Fields.EnableRefreshTooltipAfterControlValueChanged) then -- Don't include the index of the item info if we are not allowed to update the tooltip after the item's index changes.
    if (text == nil) or (text == "") then
      text = ""
    else
      text = text.."\n\n"
    end
    local color = EasyOptionsDB["TooltipAlternateTextColor"]
    local prefix = EasyOptions:ConvertTextToColoredText(L("Current Index:"), color)
    text = text..L("{1} {2} of {3}", prefix, index, numberOfDropDownItems)
  end
  control.TooltipInfo.Text = text
end
function EasyOptions:UpdateEditableDropDownItemInfosInAddonDB(dropDown)
  if (dropDown == nil) then
    return
  end
  if (not dropDown.IsEditableDropDown) then
    return
  end

  local control = dropDown.Parent.Parent -- NOTE: dropDown.Parent is the DropDownParent, dropDown.Parent.Parent is the EditableDropDown.
  if (control.BaseType ~= "EditableDropDown") then
    return
  end

  local optionItem = control.OptionItem
  if (optionItem == nil) or (optionItem.Name == nil) then
    return
  end

  local canStoreInAddonDB, addonDB, optionName = EasyOptions:CanStoreEditableDropDownItemInfosInAddonDB(optionItem)
  if (not canStoreInAddonDB) then
    return
  end

  local dropDownItemInfos = dropDown.DropDownItemInfos
  local allowAreApproximatelyEqual = false
  if (EasyOptions:AreEqual(dropDownItemInfos, optionItem.DropDownItemInfos)) then
    dropDownItemInfos = nil -- Set dropDownItemInfos to nil if dropDownItemInfos is equal to the original DropDownItemInfos so the entry in the AddonDB will be cleared.
  elseif (dropDownItemInfos == nil) then
    dropDownItemInfos = EasyOptions.Fields.NilValueIndicator -- IMPORTANT: We must set dropDownItemInfos = EasyOptions.Fields.NilValueIndicator if dropDownItemInfos is nil because an entry of nil for addonDB[optionName] indicates that the original DropDownItemInfos should be used.
  end
  
  -- We do not call EasyOptions:SetOptionValue from this method for the following reasons:
  --  EasyOptions:SetOptionValue requires an OptionItem to be associated with optionName, but no OptionItem is associated with optionName because optionName and AddonDB[optionName] are created and managed by EasyOptions.
  --  EasyOptions:SetOptionValue calls EasyOptions:HandleOptionValueChanged, but we do not want to raise an event when AddonDB[optionName] changes because optionName and AddonDB[optionName] are created and managed by EasyOptions.
  addonDB[optionName] = dropDownItemInfos
  -- local addonName = optionItem.AddonName
  -- local value = dropDownItemInfos
  -- local allowUpdateControl = false
  -- EasyOptions:SetOptionValue(addonName, optionName, value, allowUpdateControl)
end
function EasyOptions:GetEditableDropDownItemInfosFromAddonDB(control)
  if (control == nil) then
    return nil
  end
  
  local optionItem = control.OptionItem
  if (optionItem == nil) then
    return nil
  elseif (optionItem.Name == nil) then
    return EasyOptions:CopyTable(optionItem.DropDownItemInfos) -- IMPORTANT: We copy the table to ensure that we do not edit the original table if the user edits the DropDown's items.
  end

  local canStoreInAddonDB, addonDB, optionName = EasyOptions:CanStoreEditableDropDownItemInfosInAddonDB(optionItem)
  if (not canStoreInAddonDB) then
    return EasyOptions:CopyTable(optionItem.DropDownItemInfos) -- IMPORTANT: We copy the table to ensure that we do not edit the original table if the user edits the DropDown's items.
  end

  local result = addonDB[optionName]
  if (result == nil) then
    result = EasyOptions:CopyTable(optionItem.DropDownItemInfos) -- IMPORTANT: We copy the table to ensure that we do not edit the original table if the user edits the DropDown's items.
  elseif (result == EasyOptions.Fields.NilValueIndicator) then
    result = nil
  end
  return result
end
function EasyOptions:CanStoreEditableDropDownItemInfosInAddonDB(optionItem) -- canStoreInAddonDB, addonDB, optionName
  if (optionItem == nil) then
    return false, nil, nil
  end
  
  local addonDB = optionItem.AddonDB
  if (addonDB == nil) then
    return false, nil, nil
  end

  local optionName = EasyOptions:GetEditableDropDownItemInfosOptionName(optionItem)
  if (optionName == nil) then
    return false, nil, nil
  end

  return true, addonDB, optionName
end
function EasyOptions:CanRestoreDefaultEditableDropDownItems(dropDown)
  if (dropDown == nil) then
    return false
  end

  local optionItem = dropDown.OptionItem
  if (optionItem == nil) then
    return false
  end

  local length = EasyOptions:GetLength(optionItem.DropDownItemInfos)
  return (length > 0)
end
function EasyOptions:ShouldAllowRestoreDefaultEditableDropDownItems(dropDown)
  if (not EasyOptions:CanRestoreDefaultEditableDropDownItems(dropDown)) then
    return false
  end
  
  local copyTable = false
  local dropDownItemInfosDefault = EasyOptions:GetDefaultDropDownItems(dropDown, copyTable)
  local dropDownItemInfosCurrent = dropDown.DropDownItemInfos
  
  local allowAreApproximatelyEqual = false
  return (not EasyOptions:AreEqual(dropDownItemInfosDefault, dropDownItemInfosCurrent, allowAreApproximatelyEqual))
end
function EasyOptions:RestoreDefaultEditableDropDownItems(dropDown, printMessage)
  if (not EasyOptions:CanRestoreDefaultEditableDropDownItems(dropDown)) then
    return
  end
  
  local copyTable = false -- Assume that the table will be copied elsewhere if needed.
  local dropDownItemInfosDefault = EasyOptions:GetDefaultDropDownItems(dropDown, copyTable)
  local dropDownItemInfosCurrent = dropDown.DropDownItemInfos
  
  local containsItemWithCustomValue, index = EasyOptions:ContainsItemWithCustomValueInDropDownItemInfos(dropDownItemInfosCurrent, dropDownItemInfosDefault)
  if (not containsItemWithCustomValue) then
    local preserveItemsWithCustomValues = false
    EasyOptions:RestoreDefaultEditableDropDownItems_Internal(dropDown, dropDownItemInfosDefault, dropDownItemInfosCurrent, preserveItemsWithCustomValues, printMessage)
  else
    local itemName = EasyOptions:IIF((text ~= nil) and (text ~= ""), EasyOptions:Format("\"{1}\"", text), L("the item"))
    local promptText = L("Do you want to preserve items with custom values in the drop down ({1}), or remove those items from the drop down ({2})?\n\nRemoving items with custom values cannot be undone.", YES, NO)
    local uniqueID = "EasyOptions.RestoreDefaultEditableDropDownItems.PreserveCustomItems"
    local onYesHandler = function(userText)
      local preserveItemsWithCustomValues = true
      EasyOptions:RestoreDefaultEditableDropDownItems_Internal(dropDown, dropDownItemInfosDefault, dropDownItemInfosCurrent, preserveItemsWithCustomValues, printMessage)
    end
    local onNoHandler = function(userText)
      local preserveItemsWithCustomValues = false
      EasyOptions:RestoreDefaultEditableDropDownItems_Internal(dropDown, dropDownItemInfosDefault, dropDownItemInfosCurrent, preserveItemsWithCustomValues, printMessage)
    end
    local onCancelHandler = nil
    EasyOptions:ShowDialogYesNoCancel(promptText, uniqueID, onYesHandler, onNoHandler, onCancelHandler)
  end
end
function EasyOptions:RestoreDefaultEditableDropDownItems_Internal(dropDown, dropDownItemInfosDefault, dropDownItemInfosCurrent, preserveItemsWithCustomValues, printMessage)
  local dropDownItemInfos = EasyOptions:CopyTable(dropDownItemInfosDefault) -- init
  local hasPreservedItemsWithCustomValues = false -- init
  local hasRemovedItemsWithCustomValues = false -- init
  local length = EasyOptions:GetLength(dropDownItemInfosCurrent)
  if (length > 0) then -- IMPORTANT: We do not check the value of preserveItemsWithCustomValues in this case because we need to execute the logic in this block even if preserveItemsWithCustomValues is false to ensure that hasRemovedItemsWithCustomValues is set to the appropriate value (see below).
    local dropDownItemInfo
    local value
    local text
    local tooltipTitleText
    local tooltipText
    for i = 1, length do
      dropDownItemInfo = dropDownItemInfosCurrent[i]
      value, text, tooltipTitleText, tooltipText = EasyOptions:GetParametersFromDropDownItemInfo(dropDownItemInfo)
      if (not EasyOptions:ContainsValueInDropDownItemInfos(value, dropDownItemInfosDefault)) then
        if (preserveItemsWithCustomValues) then
          table.insert(dropDownItemInfos, dropDownItemInfo)
          hasPreservedItemsWithCustomValues = true
        else
          hasRemovedItemsWithCustomValues = true
          break
        end
      end
    end
  end
  
  local optionItem = dropDown.OptionItem
  local defaultValue
  if (optionItem ~= nil) then
    defaultValue = optionItem.DefaultValue
  elseif (EasyOptions:GetLength(dropDownItemInfos) >= 1) then
    defaultValue = dropDownItemInfos[1]
  else
    defaultValue = nil
  end
  local allowSetToDefaultValue = true
  dropDown:SetDropDownItemInfos(dropDownItemInfos, defaultValue, allowSetToDefaultValue)

  if (printMessage) then
    local messageSuffix = "" -- init
    if (hasPreservedItemsWithCustomValues) then
      messageSuffix = L(" Items with custom values were preserved.")
    elseif (hasRemovedItemsWithCustomValues) then
      messageSuffix = L(" Items with custom values were removed.")
    end
    EasyOptions:PrintMessage(L("Restored the drop down's default items.{1}", messageSuffix))
  end
end
function EasyOptions:GetEditableDropDownItemInfosOptionName(optionItem)
  if (optionItem == nil) or (optionItem.Name == nil) or (optionItem.Name == "") then
    return nil
  end
  return optionItem.Name.."_EasyOptions_CustomDropDownItems"
end
function EasyOptions:SetEditableDropDownValue(control, value)
  -- Update the controls.
  local previousEnableHandleControlValueChanged = control.EnableHandleControlValueChanged -- Store the value.
  control.EnableHandleControlValueChanged = false
  control.ValueDropDown:SetValue(value)
  if (control.ValueTextBox ~= nil) then
    control.ValueTextBox:SetValue(value)
  end
  control.EnableHandleControlValueChanged = previousEnableHandleControlValueChanged -- Restore the value.
  EasyOptions:UpdateEditableDropDownControlStates(control)

  -- Update Options (if required).
  local optionValue = value
  if (not EasyOptions.Current.IsBuildingOptionFrame) then
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, optionValue, allowUpdateControl)
  end
end
function EasyOptions:GetDefaultEditableDropDownTooltipText()
  return L("Specifies the selected item.\n\nYou can use the controls to the right of this drop down to edit the items in the drop down list.")
end
