-- Copyright © 2024 @Groick (Twitch). All rights reserved.
local function L(key, ...) return EasyOptions:Localize(key, ...) end
function EasyOptions:CreateColorPicker(parent, customControlName, positionInfo, color, hasAlpha, text, tooltipTitleText, tooltipText, customSize, customBorderWidth, customLabelPositionInfo, customFontInfo, customTextColor, onClickHandler)
  -- Determine parameters.
  local defaultWidth = 16.1 -- IMPORTANT: We set the size to a value slightly greater than an integer in order to help prevent a display bug that can sometimes (i.e., rarely) cause one or more of the sides of the ColorPicker's border to not be visible.
  local defaultHeight = defaultWidth
  local ignoreCustomHeight = false
  local width, height, hasWidth, hasHeight, shouldFillWidth = EasyOptions:GetSizeFromCustomSize(customSize, ignoreCustomHeight, defaultWidth, defaultHeight, EasyOptions.Fields.ControlSizeMaxAllowed_Large, EasyOptions.Fields.ControlSizeMaxAllowed_Large, true, false)
  local borderWidth = EasyOptions:GetValueFromCustomValue("BorderWidth", customBorderWidth, 1.1, EasyOptions.Fields.ControlSizeMaxAllowed_Large) -- IMPORTANT: We set the border width to a value > 1.0 in order to help prevent a display bug that can sometimes (i.e., rarely) cause one or more of the sides of the ColorPicker's border to not be visible.
  local backgroundTextureOuterPadding = 0
  local backgroundTextureInnerPadding = 0.5 * borderWidth
  local colorTexturePadding = borderWidth
  local checkersTexturePadding = colorTexturePadding
  local textureLayer = "ARTWORK"
  local onClickHandlerCustom = onClickHandler
  local onClickHandlerLocal = function(self, button)
    EasyOptions:ColorPicker_OnClickHandler(self, button)
    if (onClickHandlerCustom ~= nil) then
      onClickHandlerCustom(self, button)
    end
  end

  local controlName = EasyOptions:GetControlName("ColorPicker", customControlName)
  local control = CreateFrame("Frame", controlName, parent, "BackdropTemplateMixin" and "BackdropTemplate")
  control.Name = controlName
  control.Type = "ColorPicker"
  control.BaseType = "ColorPicker"
  local tooltipText_ColorPicker
  if (tooltipText == nil) or (tooltipText == "") then
    tooltipText_ColorPicker = ""
  else
    tooltipText_ColorPicker = tooltipText.."\n\n"
  end
  tooltipText_ColorPicker = tooltipText_ColorPicker..L("Click this item to open the Color Picker.")
  EasyOptions:SetTooltip(control, control, tooltipTitleText, tooltipText_ColorPicker)
  control:SetSize(width, height)
  control:EnableMouse(true)
  -- control:SetScript("OnEnter", onEnterHandler)
  -- control:SetScript("OnLeave", onLeaveHandler)
  -- control:SetScript("OnClick", onClickHandlerLocal)
  control:SetScript("OnMouseDown", onClickHandlerLocal)
  
  local colorTexture = control:CreateTexture(controlName..".ColorTexture", textureLayer)
  colorTexture:SetTexture("Interface\\AddOns\\EasyOptions\\Textures\\SolidColorAlpha255.tga")
  -- colorTexture:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
  colorTexture:SetSize(math.max(width - 2 * colorTexturePadding, 0), math.max(height - 2 * colorTexturePadding, 0))
  colorTexture:SetPoint("TOPLEFT", control, "TOPLEFT", colorTexturePadding, -colorTexturePadding) -- NOTE: We set the TOPLEFT and BOTTOMRIGHT rather than the CENTER so this texture will automatically resize if the control is resized.
  colorTexture:SetPoint("BOTTOMRIGHT", control, "BOTTOMRIGHT", -colorTexturePadding, colorTexturePadding)
  -- colorTexture:SetPoint("LEFT", control, "LEFT", colorTexturePadding, 0)
  control.ColorTexture = colorTexture
  control.HasAlpha = hasAlpha
  --
  control.GetColor = function(self)
    -- IMPORTANT: We must not return control.ColorTexture:GetVertexColor() because that color could be desaturated if self is disabled.
    return self.Color
  end
  control.SetColor = function(self, value)
    EasyOptions:SetColorPickerColor(self, value)
  end
  control.SetValue = control.SetColor
  control.GetColorValue = function(self, colorIndex, convertTo8bit)
    local color = self:GetColor()
    return EasyOptions:GetColorValueFromColor(color, colorIndex, convertTo8bit)
  end
  control.SetColorValue = function(self, value, colorIndex, is8bit)
    local color = self:GetColor()
    color = EasyOptions:SetColorValueInColor(color, value, colorIndex, is8bit)
    self:SetColor(color)
  end
  control.UpdateValueBoxes = function(self)
    local length = EasyOptions:GetLength(self.ValueBoxes)
    local valueBox
    local color = self:GetColor()
    local colorValue
    for i = 1, length do
      valueBox = self.ValueBoxes[i]
      colorValue = EasyOptions:GetColorValueFromColor(color, valueBox.ColorIndex, valueBox.Is8bit)
      valueBox:SetValue(colorValue)
    end
  end

  local backgroundTextureOuter = control:CreateTexture(controlName..".BackgroundTextureOuter", textureLayer) -- Specifies a texture that is filled by a solid color, but is slightly larger than the other textures so it acts like an outer border.
  backgroundTextureOuter:SetColorTexture((204 / 255), (204 / 255), (204 / 255))
  backgroundTextureOuter:SetSize(math.max(width - 2 * backgroundTextureOuterPadding, 0), math.max(height - 2 * backgroundTextureOuterPadding, 0))
  backgroundTextureOuter:SetPoint("TOPLEFT", control, "TOPLEFT", backgroundTextureOuterPadding, -backgroundTextureOuterPadding) -- NOTE: We set the TOPLEFT and BOTTOMRIGHT rather than the CENTER so this texture will automatically resize if the control is resized.
  backgroundTextureOuter:SetPoint("BOTTOMRIGHT", control, "BOTTOMRIGHT", -backgroundTextureOuterPadding, backgroundTextureOuterPadding)
  -- backgroundTextureOuter:SetPoint("CENTER", colorTexture)
  control.BackgroundTextureOuter = backgroundTextureOuter
  
  local backgroundTextureInner = control:CreateTexture(controlName..".BackgroundTextureInner", textureLayer) -- Specifies a texture that is filled by a solid color, but is slightly larger than the other textures (except backgroundTextureOuter) so it acts like an inner border.
  backgroundTextureInner:SetColorTexture((64 / 255), (64 / 255), (64 / 255))
  backgroundTextureInner:SetSize(math.max(width - 2 * backgroundTextureInnerPadding, 0), math.max(height - 2 * backgroundTextureInnerPadding, 0))
  backgroundTextureInner:SetPoint("TOPLEFT", control, "TOPLEFT", backgroundTextureInnerPadding, -backgroundTextureInnerPadding) -- NOTE: We set the TOPLEFT and BOTTOMRIGHT rather than the CENTER so this texture will automatically resize if the control is resized.
  backgroundTextureInner:SetPoint("BOTTOMRIGHT", control, "BOTTOMRIGHT", -backgroundTextureInnerPadding, backgroundTextureInnerPadding)
  -- backgroundTextureInner:SetPoint("CENTER", colorTexture)
  control.BackgroundTextureInner = backgroundTextureInner
  
  local checkersTexture = control:CreateTexture(controlName..".CheckersTexture", textureLayer)
  checkersTexture:SetTexture("Interface\\AddOns\\EasyOptions\\Textures\\CheckersHighRes.tga")
  -- checkersTexture:SetTexture(188523) -- Tileset\\Generic\\Checkers
  -- checkersTexture:SetTexCoord(0.25, 0.0, 0.5, 0.25)
  checkersTexture:SetDesaturated(true)
  checkersTexture:SetVertexColor((255 / 255), (255 / 255), (255 / 255), (64 / 255))
  checkersTexture:SetSize(math.max(width - 2 * checkersTexturePadding, 0), math.max(height - 2 * checkersTexturePadding, 0))
  checkersTexture:SetPoint("TOPLEFT", control, "TOPLEFT", checkersTexturePadding, -checkersTexturePadding) -- NOTE: We set the TOPLEFT and BOTTOMRIGHT rather than the CENTER so this texture will automatically resize if the control is resized.
  checkersTexture:SetPoint("BOTTOMRIGHT", control, "BOTTOMRIGHT", -checkersTexturePadding, checkersTexturePadding)
  -- checkersTexture:SetPoint("CENTER", colorTexture)
  control.CheckersTexture = checkersTexture
  
  -- Set the sub level of each texture to ensure that they are drawn with the appropriate z-order.
  backgroundTextureOuter:SetDrawLayer(textureLayer, 0)
  backgroundTextureInner:SetDrawLayer(textureLayer, 1)
  checkersTexture:SetDrawLayer(textureLayer, 2)
  colorTexture:SetDrawLayer(textureLayer, 3)
  
  control:SetColor(color) -- IMPORTANT: This must be called after all required properties (e.g., control.HasAlpha) have been set.

  -- Create the label for the control.
  local labelOffsetX = -3
  local labelOffsetY = 6 + math.max(0.5 * (18 - height), 0) -- Adjust the offset of the label to ensure that the label does not intersect the ValueBoxes if the height of the ColorPicker is less than the height of the ValueBoxes.
  local labelPositionInfoDefault = {"BOTTOMLEFT", "TOPLEFT", labelOffsetX, labelOffsetY} -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local labelPositionInfo = EasyOptions:GetPositionInfoFromOptionPositionInfo(labelPositionInfoDefault, customLabelPositionInfo, false)
  local fontObjectName = EasyOptions.Fields.DefaultLabelFontObjectName
  local labelControl, label, labelControlRegionWidth, labelControlRegionHeight = EasyOptions:CreateLabelForControl(parent, control, backgroundTextureOuter, 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 + 0 -- + labelOffsetX
    labelRegionHeight = labelControlRegionHeight + labelOffsetY + 0 -- 2
  end

  -- Create the color value controls.
  local colorValueLabelControl, colorValueControl1, colorValueControl2
  local colorValueControlsRegionWidth = 0 -- init
  local colorValueControlsRegionHeight = 0 -- init
  local anchorControl, colorChannelName, colorValue
  control.ValueBoxes = {}
  --
  -- Red
  anchorControl = control
  colorChannelName = "Red"
  colorValueLabelControl, colorValueControl1, colorValueControl2, colorValueControlsRegionWidth, colorValueControlsRegionHeight = EasyOptions:CreateColorPicker_CreateValueBoxPair(parent, control, anchorControl, tooltipTitleText, colorChannelName, color, colorValueControlsRegionWidth, colorValueControlsRegionHeight)
  --
  -- Green
  anchorControl = colorValueControl2
  colorChannelName = "Green"
  colorValueLabelControl, colorValueControl1, colorValueControl2, colorValueControlsRegionWidth, colorValueControlsRegionHeight = EasyOptions:CreateColorPicker_CreateValueBoxPair(parent, control, anchorControl, tooltipTitleText, colorChannelName, color, colorValueControlsRegionWidth, colorValueControlsRegionHeight)
  --
  -- Blue
  anchorControl = colorValueControl2
  colorChannelName = "Blue"
  colorValueLabelControl, colorValueControl1, colorValueControl2, colorValueControlsRegionWidth, colorValueControlsRegionHeight = EasyOptions:CreateColorPicker_CreateValueBoxPair(parent, control, anchorControl, tooltipTitleText, colorChannelName, color, colorValueControlsRegionWidth, colorValueControlsRegionHeight)
  --
  -- Alpha
  if (hasAlpha) then
    anchorControl = colorValueControl2
    colorChannelName = "Alpha"
    colorValueLabelControl, colorValueControl1, colorValueControl2, colorValueControlsRegionWidth, colorValueControlsRegionHeight = EasyOptions:CreateColorPicker_CreateValueBoxPair(parent, control, anchorControl, tooltipTitleText, colorChannelName, color, colorValueControlsRegionWidth, colorValueControlsRegionHeight)
  end

  local offsetX = 5 -- Offset the x-position of this control type to help it align better with the other controls.
  local offsetY = 5 + 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
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("is a ColorPicker that has a width of \"{1}\" which is not supported for this control type.", EasyOptions.Fields.FillWidthFlag))
    -- EasyOptions:SetControlPointToFillWidth(control, parent, positionInfo, offsetX, 5)
  end
  -- control:SetWidth(width + labelOffsetX + label:GetWidth()) -- NOTE: We don't set the size of the control to include the label's text in case a custom position is used for the label's text.
  
  -- control:SetScript("OnClick", onClickHandler)
  control.OnEnabledChangedHandler = function(self)
    EasyOptions:UpdateColorPickerVertexColor(self)
  end
  EasyOptions:AddSupportForEnabledState(control)
  
  local widthEffective = width + 0
  local heightEffective = height + 1
  local controlRegionWidth = math.max(widthEffective + colorValueControlsRegionWidth, labelRegionWidth)
  local controlRegionHeight = 0.5 * heightEffective + 0.5 * math.max(heightEffective, colorValueControlsRegionHeight) + labelRegionHeight + EasyOptions:IIF(hasLabelText, 0, 0) -- IMPORTANT: We don't use control:GetHeight() because some controls don't implement that method and most controls that support that method return a value that is larger than desired.
  control.ControlRegionWidth = controlRegionWidth
  control.ControlRegionHeight = controlRegionHeight
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateColorPicker_CreateValueBoxPair(parent, colorPicker, anchorControl, tooltipTitleText, colorChannelName, color, controlRegionWidthTotal, controlRegionHeightTotal)
  local controlTypeName = "ValueBox"
  local controlName = colorPicker.Name.."."..colorChannelName
  local customPositionInfo = nil
  local isNumeric = true
  local isMultiLine = false
  local delimiterInfo = nil
  local customFontInfo = nil
  local customTextColor = nil
  local onTextChangedHandler = function(self)
    EasyOptions:ColorPicker_ValueBox_OnTextChangedHandler(self)
  end
  local onTextAcceptedHandler = function(self)
    EasyOptions:ColorPicker_ValueBox_OnTextAcceptedHandler(self)
  end
  local onEscapePressedHandler = function(self)
    EasyOptions:ColorPicker_ValueBox_OnEscapePressedHandler(self)
  end
  
  local text = nil -- init. Specifies the text for the label.
  local tooltipTitleTextSuffix = nil
  local tooltipText = nil
  local labelTextColor = nil
  local colorIndex = nil
  if (colorChannelName == "Red") then
    text = "R"
    tooltipTitleTextSuffix = L("Red Channel")
    labelTextColor = {(241 / 255), (99 / 255), (99 / 255)}
    colorIndex = 1
  elseif (colorChannelName == "Green") then
    text = "G"
    tooltipTitleTextSuffix = L("Green Channel")
    labelTextColor = {(99 / 255), (241 / 255), (99 / 255)}
    colorIndex = 2
  elseif (colorChannelName == "Blue") then
    text = "B"
    tooltipTitleTextSuffix = L("Blue Channel")
    labelTextColor = {(99 / 255), (99 / 255), (241 / 255)}
    colorIndex = 3
  elseif (colorChannelName == "Alpha") then
    text = "A"
    tooltipTitleTextSuffix = L("Alpha Channel")
    labelTextColor = {(170 / 255), (170 / 255), (170 / 255)}
    colorIndex = 4
  else
    text = ""
    EasyOptions:PrintErrorMessageOnce(L("The name \"{1}\" is an unknown color channel.", EasyOptions:FormatName(colorChannelName)))
  end
  --
  local tooltipTitleTextLocal = tooltipTitleText or ""
  if (tooltipTitleTextSuffix ~= nil) and (tooltipTitleTextSuffix ~= "") then
    tooltipTitleTextSuffix = EasyOptions:ConvertTextToColoredText(tooltipTitleTextSuffix, labelTextColor)
    if (tooltipTitleTextLocal == nil) or (tooltipTitleTextLocal == "") then
      tooltipTitleTextLocal = tooltipTitleTextSuffix
    else
      tooltipTitleTextLocal = tooltipTitleTextLocal.." ("..tooltipTitleTextSuffix..")"
    end
  end

  -- Create the label.
  local labelOffsetX = math.min(0, 0.5 * EasyOptions:GetDefaultControlPaddingX())
  local labelOffsetY = -2.5 -- NOTE: This labelOffsetY uses the opposite sign (see below) of other labelOffsetY values in this addon because this offset represents the offset that is required to align the label vertically with the ColorPicker rather than the distance above the control.
  local labelPositionInfoDefault = {"LEFT", "RIGHT", labelOffsetX, -labelOffsetY} -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local labelPositionInfo = EasyOptions:GetPositionInfoFromOptionPositionInfo(labelPositionInfoDefault, customPositionInfo, false)
  local fontObjectName = EasyOptions.Fields.DefaultLabelFontObjectName
  local labelControl, label, labelControlRegionWidth, labelControlRegionHeight = EasyOptions:CreateLabelForControl(parent, nil, anchorControl, controlName, text, tooltipTitleText, tooltipText, labelPositionInfo, fontObjectName, customFontInfo, labelTextColor)
  label:SetJustifyH("RIGHT") -- "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 + labelOffsetX + 0
    labelRegionHeight = labelControlRegionHeight + 0 -- - labelOffsetY
  end

  -- Create the ValueBoxes.
  local tooltipTextSuffix = L("\n\nYou can enter the desired value for this channel directly or click on the box that displays the color to open a ColorPicker.")
  --
  local controlName1 = controlName..controlTypeName.."1"
  local value1 = EasyOptions:GetColorValueFromColor(color, colorIndex, false)
  local minValue1 = 0.0
  local maxValue1 = 1.0
  local formatString1 = "%.3f"
  local text1 = nil -- Specifies the text for the label.
  local tooltipText1 = L("Specifies the value of the {1} channel expressed as a value between 0.0 - 1.0, inclusive."..tooltipTextSuffix, colorChannelName)
  local customSize1 = 40
  local controlName2 = controlName..controlTypeName.."2"
  local value2 = EasyOptions:GetColorValueFromColor(color, colorIndex, true)
  local minValue2 = 0.0
  local maxValue2 = 255.0
  local formatString2 = "%i"
  local text2 = nil -- Specifies the text for the label.
  local tooltipText2 = L("Specifies the value of the {1} channel expressed as a value between 0 - 255, inclusive."..tooltipTextSuffix, colorChannelName)
  local customSize2 = 30 -- customSize1
  --
  local offsetX1 = 4
  local offsetY1 = -labelOffsetY -- Correct for the offset of the LabelControl so this offset value is relative to the ColorPicker.
  local offsetX2 = 5
  local offsetY2 = 0
  local positionInfoDefault1 = {"LEFT", "RIGHT", offsetX1, -offsetY1}
  local positionInfoDefault2 = {"LEFT", "RIGHT", offsetX2, -offsetY2}
  local positionInfo1 = EasyOptions:GetPositionInfoFromOptionPositionInfo(positionInfoDefault1, customPositionInfo, false)
  local positionInfo2 = EasyOptions:GetPositionInfoFromOptionPositionInfo(positionInfoDefault2, customPositionInfo, false)
  local control1, width1, height1 = EasyOptions:CreateTextBox(parent, controlName1, value1, minValue1, maxValue1, formatString1, text1, nil, nil, positionInfo1, customSize1, isNumeric, isMultiLine, delimiterInfo, customFontInfo, customTextColor, onTextChangedHandler, onTextAcceptedHandler, onEscapePressedHandler)
  local control2, width2, height2 = EasyOptions:CreateTextBox(parent, controlName2, value2, minValue2, maxValue2, formatString2, text2, nil, nil, positionInfo2, customSize2, isNumeric, isMultiLine, delimiterInfo, customFontInfo, customTextColor, onTextChangedHandler, onTextAcceptedHandler, onEscapePressedHandler)
  control1:ClearAllPoints()
  control1:SetPoint(positionInfo1[1], labelControl, positionInfo1[2], positionInfo1[3], positionInfo1[4]) -- NOTE: We offset the x-position of this control type to help it align better with the other controls.
  EasyOptions:SetTooltip(control1, control1, tooltipTitleTextLocal, tooltipText1) -- NOTE: We set the tooltip in this method because we want the ValueBox's tooltip to include the text of the ColorPicker's label, but we do not want the ValueBox's label to contain that text.
  control2:ClearAllPoints()
  control2:SetPoint(positionInfo2[1], control1, positionInfo2[2], positionInfo2[3], positionInfo2[4]) -- NOTE: We offset the x-position of this control type to help it align better with the other controls.
  EasyOptions:SetTooltip(control2, control2, tooltipTitleTextLocal, tooltipText2) -- NOTE: We set the tooltip in this method because we want the ValueBox's tooltip to include the text of the ColorPicker's label, but we do not want the ValueBox's label to contain that text.
  
  -- Set control properties.
  control1.ColorChannelName = colorChannelName
  control1.Is8bit = false
  control2.ColorChannelName = colorChannelName
  control2.Is8bit = true
  if (colorChannelName == "Red") then
    colorPicker.RedLabelControl = labelControl
    colorPicker.RedLabel = label
    colorPicker.RedValueBox1 = control1
    colorPicker.RedValueBox2 = control2
  elseif (colorChannelName == "Green") then
    colorPicker.GreenLabelControl = labelControl
    colorPicker.GreenLabel = label
    colorPicker.GreenValueBox1 = control1
    colorPicker.GreenValueBox2 = control2
  elseif (colorChannelName == "Blue") then
    colorPicker.BlueLabelControl = labelControl
    colorPicker.BlueLabel = label
    colorPicker.BlueValueBox1 = control1
    colorPicker.BlueValueBox2 = control2
  elseif (colorChannelName == "Alpha") then
    colorPicker.AlphaLabelControl = labelControl
    colorPicker.AlphaLabel = label
    colorPicker.AlphaValueBox1 = control1
    colorPicker.AlphaValueBox2 = control2
  else
    EasyOptions:PrintErrorMessageOnce(L("The name \"{1}\" is an unknown color channel.", EasyOptions:FormatName(colorChannelName)))
  end
  --
  control1.ColorPicker = colorPicker
  control2.ColorPicker = colorPicker
  control1.ColorIndex = colorIndex
  control2.ColorIndex = colorIndex
  control1.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  control2.AllowSetOptionValue = false -- Only update Options when the value of the parent control changes.
  table.insert(colorPicker.ValueBoxes, control1)
  table.insert(colorPicker.ValueBoxes, control2)
  EasyOptions:AddControlsToOwner({labelControl, control1, control2}, colorPicker)
  EasyOptions:SetIsSubOptionItemControl({control1, control2})

  local controlRegionWidth = labelRegionWidth + width1 + width2 + offsetX1
  local controlRegionHeight = math.max(labelRegionHeight, math.max(height1, height2)) -- NOTE: We don't use control:GetHeight() because some controls don't implement that method and most controls that support that method return a value that is larger than desired.
  return labelControl, control1, control2, controlRegionWidth + controlRegionWidthTotal, math.max(controlRegionHeight, controlRegionHeightTotal)
end
function EasyOptions:ColorPicker_OnClickHandler(control, button)
  if (button == "LeftButton") and (EasyOptions:GetEnabledStateOfControl(control)) then
    EasyOptions:ShowColorPicker(control)
  end
end
function EasyOptions:ColorPicker_ValueBox_OnTextChangedHandler(control)
  -- Reserved.
end
function EasyOptions:ColorPicker_ValueBox_OnTextAcceptedHandler(control)
  local optionItem = control.OptionItem
  local addonDB = nil -- init
  local optionName = nil -- init
  if (optionItem ~= nil) then
    addonDB = optionItem.AddonDB
    optionName = optionItem.Name
  end
  --
  local colorPicker = control.ColorPicker
  local colorIndex = control.ColorIndex
  local is8bit = control.Is8bit

  local alternateValue
  if (addonDB == nil) or (optionName == nil) then
    alternateValue = colorPicker:GetColorValue(colorIndex, is8bit)
  else
    alternateValue = EasyOptions:GetColorValueFromColor(addonDB[optionName], colorIndex, is8bit) -- Use the current value in Options as the alternate value to use if the TextBox does not represent a numeric value.
  end
  local value = EasyOptions:GetTextBoxValueFromText(control, alternateValue)
  colorPicker:SetColorValue(value, colorIndex, is8bit) -- Set the value of the colorPicker.
end
function EasyOptions:ColorPicker_ValueBox_OnEscapePressedHandler(control)
  local colorPicker = control.ColorPicker
  local colorIndex = control.ColorIndex
  local is8bit = control.Is8bit
  local value = colorPicker:GetColorValue(colorIndex, is8bit)
  control:SetValue(value)
end
function EasyOptions:CreateOptionColorPicker(parent, positionInfo, optionItem)
  local optionName = optionItem.Name
  local value = EasyOptions:GetOptionValueOrDefaultValue(optionName, optionItem.DefaultValue)
  local hasAlpha = EasyOptions:IIF((optionItem.DefaultValue ~= nil) and (optionItem.DefaultValue[4] ~= nil), true, false) -- IMPORTANT: We must determine hasAlpha from the default value, not the current value in the database.
  local text = EasyOptions:GetControlText(optionItem)
  local tooltipTitleText = optionItem.TooltipTitleText or text
  local tooltipText = optionItem.TooltipText
  local customSize = optionItem.Size
  local customBorderWidth = optionItem.BorderWidth
  local customLabelPositionInfo = optionItem.LabelPositionInfo
  local customFontInfo = optionItem.FontInfo
  local customTextColor = optionItem.TextColor
  local onClickHandler = function(self, button)
    EasyOptions:OptionColorPicker_OnClickHandler(self, button)
  end

  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateColorPicker(parent, nil, positionInfo, value, hasAlpha, text, tooltipTitleText, tooltipText, customSize, customBorderWidth, customLabelPositionInfo, customFontInfo, customTextColor, onClickHandler)
  EasyOptions:AddRightButtonDownHandlerForOptionControl(control)
  for i = 1, EasyOptions:GetLength(control.ValueBoxes) do
    EasyOptions:AddRightButtonDownHandlerForOptionControl(control.ValueBoxes[i])
  end

  optionItem.Control = control
  control.OptionItem = optionItem
  for i = 1, EasyOptions:GetLength(control.ValueBoxes) do
    control.ValueBoxes[i].OptionItem = optionItem
  end
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:OptionColorPicker_OnClickHandler(control, button)
  -- Reserved.
  --  See also EasyOptions:ColorPicker_OnClickHandler.
end
function EasyOptions:SetColorPickerColor(control, color)
  local red, green, blue, alpha = EasyOptions:GetColorFromCustomColor(color, control.HasAlpha)
  local colorLocal
  if (not control.HasAlpha) then
    colorLocal = {red, green, blue}
  else
    colorLocal = {red, green, blue, alpha}
  end
  control.Color = colorLocal
  EasyOptions:SetColorPickerVertexColor(control, colorLocal)
  control:UpdateValueBoxes()
  
  -- Update Options (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) then
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, colorLocal, allowUpdateControl)
  end
end
function EasyOptions:UpdateColorPickerVertexColor(control)
  local color = control:GetColor()
  EasyOptions:SetColorPickerVertexColor(control, color)
end
function EasyOptions:SetColorPickerVertexColor(control, color)
  -- NOTE: control.IsEnabled can be nil during initialization.
  local enabled = (control.IsEnabled == nil) or (control:IsEnabled()) -- Assume that the control is enabled if its IsEnabled method is nil.
  local colorLocal = EasyOptions:GetColorForControlState(enabled, color)
  local r, g, b, a = colorLocal[1], colorLocal[2], colorLocal[3], colorLocal[4] or 1.0
  control.ColorTexture:SetVertexColor(r, g, b, a) -- control.ColorTexture:SetColorTexture(r, g, b, a)
end
function EasyOptions:ShowColorPicker(control)
  local controlLocal = control
  local color = controlLocal:GetColor()
  local r, g, b, a = color[1], color[2], color[3], color[4] or 1.0
  local hasAlpha = controlLocal.HasAlpha

  -- Hide ColorPickerFrame to ensure that its OnShow event will be handled.
  ColorPickerFrame:Hide()

  -- Set frame properties.
  ColorPickerFrame.previousValues = nil -- {r, g, b, a} -- {r = r, g = g, b = b, opacity = opacity} -- Not used.
  ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG")
  ColorPickerFrame:SetClampedToScreen(true)
  --
  -- IMPORTANT: We must clear the ColorPickerFrame's callback methods before we call ColorPickerFrame:SetColorRGB because calling ColorPickerFrame:SetColorRGB will execute ColorPickerFrame.func.
  --  i.e., If we don't clear the ColorPickerFrame's callback methods then the color of the previously edited controlLocal will be changed when we call ColorPickerFrame:SetColorRGB in this method because ColorPickerFrame.func will still be set to the callback for that controlLocal.
  ColorPickerFrame.func = nil
  ColorPickerFrame.opacityFunc = nil
  ColorPickerFrame.cancelFunc = nil

  local info = {}
  info.r, info.g, info.b, info.a = r, g, b, a -- NOTE: Setting the info.a property is optional and it is not used by the WoW API.
  info.opacity = EasyOptions:GetColorPickerFrameOpacityValueFromAlphaValue(a)
  info.hasAlpha = hasAlpha -- NOTE: Setting the info.hasAlpha property is optional and it is not used by the WoW API.
  info.hasOpacity = hasAlpha
  info.extraInfo = nil
  --
  -- Callback methods.
  info.func = function() -- NOTE: Setting the info.func property is optional and it is not used by the WoW API, but it is set regardless because it corresponds to ColorPickerFrame.func.
    local r, g, b = ColorPickerFrame:GetColorRGB()
    local a = EasyOptions:GetColorPickerFrameAlpha()
    EasyOptions:ColorPickerCallback(controlLocal, r, g, b, a)
  end
  info.swatchFunc = info.func -- This corresponds to ColorPickerFrame.func.
  info.opacityFunc = info.func -- Use the same function when ColorPickerFrame's color value changes and its opacity (or alpha) value changes.
  info.cancelFunc = function()
    EasyOptions:ColorPickerCallback(controlLocal, r, g, b, a) -- Pass the local values from this method rather than values from ColorPickerFrame.
  end

  -- Show ColorPickerFrame.
  if (not EasyOptions:UseLegacyLogicForColorPickerFrame()) then
    ColorPickerFrame:SetupColorPickerAndShow(info) -- OpenColorPicker was moved to ColorPickerFrame:SetupColorPickerAndShow in v10.2.5. See also https://warcraft.wiki.gg/wiki/Patch_10.2.5/API_changes#Color_picker_changes.
  else
    OpenColorPicker(info)
  end
end
function EasyOptions:GetColorPickerFrameAlpha()
  if (not EasyOptions:UseLegacyLogicForColorPickerFrame()) then
    return EasyOptions:IIF(ColorPickerFrame.hasOpacity, ColorPickerFrame:GetColorAlpha(), 1.0)
  else
    return EasyOptions:IIF(ColorPickerFrame.hasOpacity, 1.0 - OpacitySliderFrame:GetValue(), 1.0)
  end
end
function EasyOptions:GetColorPickerFrameOpacityValueFromAlphaValue(a)
  if (not EasyOptions:UseLegacyLogicForColorPickerFrame()) then
    return a -- NOTE: info.opacity (which is passed to ColorPickerFrame:SetupColorPickerAndShow(info), or previously to OpenColorPicker(info)) should be set to the desired alpha value (rather than the opacity value) as of v10.2.5.
  else
    return 1.0 - a
  end
end
function EasyOptions:UseLegacyLogicForColorPickerFrame()
  return false
  -- local version, build, date, tocversion = GetBuildInfo()
  -- return (tocversion < 100205) -- Changes were made to the ColorPicker in WoW v10.2.5. See also https://warcraft.wiki.gg/wiki/Patch_10.2.5/API_changes#Color_picker_changes.
end
function EasyOptions:ColorPickerCallback(control, r, g, b, a)
  -- Update the control.
  local value = EasyOptions:RoundColor({r, g, b, a})
  local color
  if (not control.HasAlpha) and (value[4] == 1.0) then
    control:SetColor({value[1], value[2], value[3]})
  else
    control:SetColor({value[1], value[2], value[3], value[4]})
  end
end
