-- Copyright © 2023 @Groick (Twitch). All rights reserved.
local function L(key, ...) return EasyOptions:Localize(key, ...) end
function EasyOptions:CreateRadioButton(parent, customControlName, value, optionValue, text, tooltipTitleText, tooltipText, radioButtonGroupName, positionInfo, customFontInfo, customTextColor, onClickHandler)
  local controlName = EasyOptions:GetControlName("RadioButton", customControlName)
  local control = CreateFrame("CheckButton", controlName, parent, "UIRadioButtonTemplate")
  control.Name = controlName
  control.Type = "RadioButton"
  control.BaseType = "RadioButton"
  control.RadioButtonGroupName = radioButtonGroupName
  control.OptionValue = optionValue
  control:SetMotionScriptsWhileDisabled(true)
  -- control:EnableMouse(true)
  
  if (radioButtonGroupName ~= nil) then
    if (EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName] == nil) then
      EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName] = {}
    end
    table.insert(EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName], control)
  end

  control.GetValue = function(self)
    return EasyOptions:GetRadioButtonValue(self)
  end
  control.SetValue = function(self, value)
    local updateCheckedStateOfRadioButtonsInRadioButtonGroup = (not EasyOptions.Current.IsBuildingOptionFrame) -- NOTE: We don't update the checked state of controls if EasyOptions.Current.IsBuildingOptionFrame is true because we assume that the checked state of controls will be set to the appropriate value when each control is created.
    EasyOptions:SetRadioButtonValue(self, value, updateCheckedStateOfRadioButtonsInRadioButtonGroup)
  end

  local height = 16 -- IMPORTANT: We don't use control:GetHeight() because that function returns an invalid value for this control type.
  
  local labelOffsetX = 2
  local labelOffsetY = 0 -- 1
  local label = control:CreateFontString(controlName..".Label", "ARTWORK", EasyOptions.Fields.DefaultLabelFontObjectName)
  control.Label = label
  label:SetJustifyH("LEFT") -- "LEFT", "RIGHT", "CENTER"
  label:SetJustifyV("MIDDLE") -- "TOP", "BOTTOM", "MIDDLE"
  local labelPositionInfo = {"LEFT", "RIGHT", labelOffsetX, labelOffsetY}
  label:SetPoint(labelPositionInfo[1], control, labelPositionInfo[2], labelPositionInfo[3], labelPositionInfo[4])
  -- label:SetWidth(math.max(width, EasyOptions:GetLabelStringWidth(label, true)) + offsetX) -- Set the size in order to help prevent text from wrapping.
  EasyOptions:SetFontFromCustomFontInfo(label, EasyOptions.Fields.DefaultFontInfo, EasyOptions.Fields.DefaultFontColor) -- We must initialize the font of this control to the default font.
  EasyOptions:SetFontFromCustomFontInfo(label, customFontInfo, customTextColor)
  --
  local labelRegionWidth = 0 -- init
  local labelRegionHeight = 0 -- init
  local hasLabelText = (text ~= nil) and (text ~= "")
  if (hasLabelText) then
    label:SetText(text)
    labelRegionWidth = EasyOptions:GetLabelStringWidth(label, true) + labelOffsetX
    labelRegionHeight = EasyOptions:GetLabelStringHeight(label) + 2 -- + labelOffsetY + 0
    local hitRectHeightOffset = 0.5 * (2 - math.max(labelRegionHeight - height, 0))
    control:SetHitRectInsets(0, -1 * labelRegionWidth, hitRectHeightOffset, hitRectHeightOffset) -- 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 in order to allow the user to click the label region of the RadioButton.
    -- label:SetSize(labelRegionWidth, labelRegionHeight) -- Set the size in order to help prevent text from wrapping.
  end
  
  control:SetValue(value)
  EasyOptions:SetTooltip(control, control, tooltipTitleText, tooltipText)
  -- control.Label:SetNonSpaceWrap(false)
  
  local offsetX = 4
  local offsetY = -5
  control:ClearAllPoints()
  control:SetPoint(positionInfo[1], parent, positionInfo[2], positionInfo[3] + offsetX, positionInfo[4] + offsetY)
  
  local onClickHandlerCustom = onClickHandler
  local onClickHandlerLocal = function(self, button)
    self:SetValue(true)
    if (onClickHandlerCustom ~= nil) then
      onClickHandlerCustom(self)
    end
  end
  control:SetScript("OnClick", onClickHandlerLocal)
  control.OnEnabledChangedHandler = function(self)
    -- Reserved.
  end
  EasyOptions:AddSupportForEnabledState(control)
  
  local controlRegionWidth = (control:GetWidth() - 7) + offsetX + EasyOptions:IIF(hasLabelText, math.max(labelRegionWidth + 1, 0), 0)
  local controlRegionHeight = math.max(height, labelRegionHeight)
  control.ControlRegionWidth = controlRegionWidth
  control.ControlRegionHeight = controlRegionHeight
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionRadioButton(parent, positionInfo, optionItem)
  -- Ensure that optionItem.RadioButtonGroupName is unique for each option category.
  -- NOTE: We don't set this in EasyOptions:SetOptionItemProperties to help ensure internal consistency in case multiple control types support the RadioButtonGroupName property or if an OptionItem is passed to this method that was not initialized in EasyOptions:SetOptionItemProperties.
  if (optionItem.RadioButtonGroupNameBase == nil) then
    optionItem.RadioButtonGroupNameBase = optionItem.RadioButtonGroupName
    optionItem.RadioButtonGroupName = EasyOptions:GetRadioButtonGroupName(optionItem.RadioButtonGroupName, true)
  end

  local optionName = optionItem.Name
  local value = EasyOptions:GetOptionValueOrDefaultValue(optionName, optionItem.DefaultValue)
  local optionValue = nil
  local text = EasyOptions:GetControlText(optionItem)
  local tooltipTitleText = optionItem.TooltipTitleText or text
  local tooltipText = optionItem.TooltipText
  local radioButtonGroupName = optionItem.RadioButtonGroupName
  local customFontInfo = optionItem.FontInfo
  local customTextColor = optionItem.TextColor
  local onClickHandlerCustom = optionItem.OnClickHandler
  local onClickHandler = function(self, button)
    EasyOptions:OptionRadioButton_OnClickHandler(self)
    if (onClickHandlerCustom ~= nil) then
      onClickHandlerCustom(self, button)
    end
  end
  
  local control, controlRegionWidth, controlRegionHeight = EasyOptions:CreateRadioButton(parent, nil, value, optionValue, text, tooltipTitleText, tooltipText, radioButtonGroupName, positionInfo, customFontInfo, customTextColor, onClickHandler)
  EasyOptions:AddRightButtonDownHandlerForOptionControl(control)
  
  optionItem.Control = control
  control.OptionItem = optionItem
  -- control.RadioButtonGroupName = radioButtonGroupName -- This property is set in EasyOptions:CreateRadioButton.
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:CreateOptionRadioButtonGroup(parent, positionInfo, optionItem)
  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 radioButtonInfos = optionItem.RadioButtonInfos
  local radioButtonGroupName = EasyOptions:GetRadioButtonGroupName(optionName, false)
  local alignAsRow = (optionItem.AlignAsRow == true)
  local customFontInfo = optionItem.FontInfo
  local customTextColor = optionItem.TextColor
  local onClickHandlerCustom = optionItem.OnClickHandler
  local onClickHandler = function(self, button)
    EasyOptions:OptionRadioButton_OnClickHandler(self)
    if (onClickHandlerCustom ~= nil) then
      onClickHandlerCustom(self, button)
    end
  end

  -- Determine parameters.
  local paddingX = EasyOptions:GetDefaultControlPaddingX()
  local paddingY = EasyOptions:GetDefaultControlPaddingY()
  
  -- Create the RadioButtonParent which acts as the control that is associated with the OptionItem. See also control.RadioButtons and radioButton.RadioButtonParent.
  local controlTypeName = "RadioButtonGroupParent"
  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. See also control.RadioButtons and radioButton.RadioButtonParent.
  control.Name = controlName
  control.Type = controlTypeName
  control.BaseType = controlTypeName
  -- EasyOptions:SetTooltip(control, control, tooltipTitleText, tooltipText)
  control:EnableMouse(false)
  
  control.OnEnabledChangedHandler = function(self)
    -- Reserved.
  end
  EasyOptions:AddSupportForEnabledState(control)
  
  control.GetValue = function(self)
    return EasyOptions:GetRadioButtonParentValue(self)
  end
  control.SetValue = function(self, value)
    EasyOptions:SetRadioButtonParentValue(self, value)
  end
  
  -- Create the label for the control.
  local labelOffsetX = 0
  local labelOffsetY = 0
  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
  
  local offsetX = 0 -- Offset the x-position of this control type to help it align better with the other controls.
  local offsetY = 0 + 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)
  
  -- Create the RadioButtons.
  local isChecked = false -- NOTE: We assume that the checked state of the RadioButtons will be updated when EasyOptions:SetRadioButtonParentValue is called later in this method (see below).
  local optionValue
  local radioButtonText
  local radioButtonTooltipTitleText
  local radioButtonTooltipText
  local items = EasyOptions:GetParametersFromRadioButtonInfos(radioButtonInfos)
  local length = EasyOptions:GetLength(items)
  local radioButton, radioButtonRegionWidth, radioButtonRegionHeight
  local radioButtons = {}
  local positionInfoCurrent = {"TOPLEFT", "TOPLEFT", 0, 0} -- init -- "LEFT", "RIGHT", "TOP", "BOTTOM", "TOPLEFT", "TOPRIGHT", "BOTTOMLEFT", "BOTTOMRIGHT", "CENTER"
  local deltaX
  local deltaY
  local radioButtonRegionWidthTotal = 0 -- init
  local radioButtonRegionHeightTotal = 0 -- init
  for i = 1, length do
    optionValue = items[i].value
    radioButtonText = items[i].text
    radioButtonTooltipTitleText = items[i].tooltipTitleText
    radioButtonTooltipText = items[i].tooltipText
    radioButton, radioButtonRegionWidth, radioButtonRegionHeight = EasyOptions:CreateRadioButton(control, nil, isChecked, optionValue, radioButtonText, radioButtonTooltipTitleText, radioButtonTooltipText, radioButtonGroupName, positionInfoCurrent, customFontInfo, customTextColor, onClickHandler)
    radioButton.OptionItem = optionItem
    radioButton.RadioButtonParent = control
    radioButtons[i] = radioButton
    EasyOptions:AddRightButtonDownHandlerForOptionControl(radioButton)
    --
    if (alignAsRow) then
      deltaX = radioButtonRegionWidth + paddingX
      deltaY = 0
      radioButtonRegionWidthTotal = EasyOptions:IIF(i == 1, radioButtonRegionWidthTotal + radioButtonRegionWidth, radioButtonRegionWidthTotal + paddingX + radioButtonRegionWidth)
      radioButtonRegionHeightTotal = math.max(radioButtonRegionHeight, radioButtonRegionHeightTotal)
    else
      deltaX = 0
      deltaY = radioButtonRegionHeight + paddingY
      radioButtonRegionWidthTotal = math.max(radioButtonRegionWidthTotal, radioButtonRegionWidth)
      radioButtonRegionHeightTotal = EasyOptions:IIF(i == 1, radioButtonRegionHeightTotal + radioButtonRegionHeight, radioButtonRegionHeightTotal + paddingY + radioButtonRegionHeight)
    end
    positionInfoCurrent = {positionInfoCurrent[1], positionInfoCurrent[2], positionInfoCurrent[3] + deltaX, positionInfoCurrent[4] - deltaY}
  end
  control:SetSize(radioButtonRegionWidthTotal, radioButtonRegionHeightTotal)
  --
  optionItem.Control = control
  optionItem.RadioButtonGroupName = radioButtonGroupName
  optionItem.RadioButtons = radioButtons
  control.OptionItem = optionItem
  control.RadioButtonGroupName = radioButtonGroupName
  control.RadioButtons = radioButtons
  EasyOptions:AddControlsToParent(radioButtons, control)
  if (optionName ~= nil) and (optionName ~= "") then
    EasyOptions.Current.OptionControls[optionName] = control
  end
  --
  control:SetValue(value) -- IMPORTANT: control:SetValue must be called after all properties of control are set (e.g., control.RadioButtons).
  
  local widthEffective = math.max(radioButtonRegionWidthTotal, labelRegionWidth)
  local heightEffective = radioButtonRegionHeightTotal + labelRegionHeight
  
  local controlRegionWidth = widthEffective
  local controlRegionHeight = heightEffective
  control.ControlRegionWidth = controlRegionWidth
  control.ControlRegionHeight = controlRegionHeight
  return control, controlRegionWidth, controlRegionHeight
end
function EasyOptions:OptionRadioButton_OnClickHandler(control)
  -- Reserved.
end
function EasyOptions:GetRadioButtonGroupName(baseName, allowNil)
  if (baseName == nil) and (allowNil) then
    return nil
  end
  local rootName = EasyOptions:GetFullNameOfOptionItems(EasyOptions.Current.OptionItems)..".RadioButtonGroup"
  local groupID
  if (baseName ~= nil) and (baseName ~= "") then
    groupID = "."..baseName
  else
    EasyOptions.Fields.RadioButtonDefaultGroupIndex = EasyOptions.Fields.RadioButtonDefaultGroupIndex + 1
    groupID = EasyOptions:Format(EasyOptions.Fields.RadioButtonDefaultGroupIndex)
  end
  return rootName..groupID
end
function EasyOptions:GetParametersFromRadioButtonInfos(radioButtonInfos)
  if (radioButtonInfos ~= nil) and (type(radioButtonInfos) ~= "table") then
    EasyOptions:PrintErrorMessageWithOptionItemInfo(L("has an invalid entry for its RadioButton items. RadioButton items must be contained in a table."))
    return
  end

  local length = EasyOptions:GetLength(radioButtonInfos)
  local value
  local text
  local tooltipTitleText
  local tooltipText
  local items = {}
  for i = 1, length do
    value, text, tooltipTitleText, tooltipText = EasyOptions:GetParametersFromDropDownItemInfo(radioButtonInfos[i]) -- NOTE: RadioButtonInfos use the same format as DropDownItemInfos.
    items[i] = {}
    items[i].value = value
    items[i].text = text
    items[i].tooltipTitleText = tooltipTitleText
    items[i].tooltipText = tooltipText
  end
  return items
end
function EasyOptions:GetRadioButtonValue(control)
  return control:GetChecked()
end
function EasyOptions:SetRadioButtonValue(control, isChecked, updateCheckedStateOfRadioButtonsInRadioButtonGroup)
  -- Update the control.
  control:SetChecked(isChecked)

  -- Update Options (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) and ((control.OptionValue == nil) or (isChecked)) then -- IMPORTANT: If control.OptionValue is nil then always set addonDB[optionName] = isChecked, otherwise only set addonDB[optionName] = control.OptionValue if isChecked is true.
    local optionValue = EasyOptions:IIF(control.OptionValue ~= nil, control.OptionValue, isChecked) -- Set the value in Options to isChecked if control.OptionValue is nil.
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, optionValue, allowUpdateControl)
  end

  -- Update the enabled state of controls (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) then -- NOTE: We don't update the enabled state of controls if EasyOptions.Current.IsBuildingOptionFrame is true because we assume that the enabled state of controls will be set to the appropriate value when each Option frame is built.
    local includeChildControls = false -- Don't update grandchild controls because we assume that each child control will update the enabled state of its children if appropriate.
    EasyOptions:UpdateEnabledStateOfChildControlsFromEnabledStateOfOwners(control, includeChildControls)
  end

  -- Update the checked state of the other RadioButtions in the RadioButtionGroup (if required).
  if (updateCheckedStateOfRadioButtonsInRadioButtonGroup) then
    EasyOptions:UpdateCheckedStateOfRadioButtonsInRadioButtonGroup(control)
  end
end
function EasyOptions:GetRadioButtonParentValue(control)
  local radioButtons = control.RadioButtons
  local length = EasyOptions:GetLength(radioButtons)
  local radioButton
  local isChecked
  for i = 1, length do
    radioButton = radioButtons[i]
    isChecked = radioButton:GetValue()
    if (isChecked) then
      return radioButton.OptionValue or i
    end
  end
  return nil
  -- IMPORTANT: The following logic is not used in this method because this method must return the value that is currently represented by the control, not the value that is currently stored in Options.
  -- local optionItem = control.OptionItem
  -- local addonDB = nil -- init
  -- local optionName = nil -- init
  -- if (optionItem ~= nil) then
  --   addonDB = optionItem.AddonDB
  --   optionName = optionItem.Name
  -- end
  -- if (addonDB == nil) or (optionName == nil) then
  --   return nil
  -- end
  -- return addonDB[optionName]
end
function EasyOptions:SetRadioButtonParentValue(control, value)
  -- Update the checked state of the RadioButtons.
  local radioButtons = control.RadioButtons
  local length = EasyOptions:GetLength(radioButtons)
  local radioButton
  local isChecked
  local updateCheckedStateOfRadioButtonsInRadioButtonGroup = false
  for i = 1, length do
    radioButton = radioButtons[i]
    isChecked = (radioButton.OptionValue == value)
    EasyOptions:SetRadioButtonValue(radioButton, isChecked, updateCheckedStateOfRadioButtonsInRadioButtonGroup)
  end

  -- Update Options (if required).
  if (not EasyOptions.Current.IsBuildingOptionFrame) then
    local allowUpdateControl = false
    EasyOptions:SetOptionValueFromControlInfo(control, value, allowUpdateControl)
  end
end
function EasyOptions:GetRadioButtonWithDefaultCheckedStateOfTrue(control)
  if (EasyOptions:GetDefaultCheckedStateOfRadioButton(control)) then
    return control
  end

  local radioButtonGroupName = control.RadioButtonGroupName
  if (radioButtonGroupName == nil) then
    return nil
  end

  local radioButtons = EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName]
  local length = EasyOptions:GetLength(radioButtons)
  if (length == 0) then
    return nil
  end

  local radioButton
  for i = 1, length do
    radioButton = radioButtons[i]
    if (EasyOptions:GetDefaultCheckedStateOfRadioButton(radioButton)) then
      return radioButton
    end
  end
  return nil
end
function EasyOptions:GetDefaultCheckedStateOfRadioButton(control)
  local optionItem = control.OptionItem
  if (optionItem == nil) then
    return control:GetValue()
  end
  
  local controlType = optionItem.ControlType
  if (controlType == "RadioButton") then
    return optionItem.DefaultValue
  elseif (controlType == "RadioButtonGroup") then
    return (optionItem.DefaultValue == control.OptionValue)
  else
    EasyOptions:PrintErrorMessageOnce(L("\"{1}\" is an unknown RadioButton control type.", EasyOptions:FormatName(controlType)))
    return control:GetValue()
  end
end
function EasyOptions:IsRadioButtonGroupOfOptionItemSetToDefaultValue(optionItem)
  local defaultResult = true -- Return true by default.

  if (optionItem == nil) then
    return defaultResult
  end
  
  local radioButtonGroupName = optionItem.RadioButtonGroupName
  if (radioButtonGroupName == nil) then
    return defaultResult
  end

  local radioButtons = EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName]
  local length = EasyOptions:GetLength(radioButtons)
  if (length == 0) then
    return defaultResult
  end

  local radioButton
  for i = 1, length do
    radioButton = radioButtons[i]
    if (radioButton:GetValue() ~= EasyOptions:GetDefaultCheckedStateOfRadioButton(radioButton)) then
      return false
    end
  end
  return true
end
function EasyOptions:UpdateCheckedStateOfRadioButtonsInRadioButtonGroup(control)
  local radioButtonGroupName = control.RadioButtonGroupName
  if (radioButtonGroupName == nil) then
    return
  end

  local radioButtons = EasyOptions.Fields.RadioButtonGroups[radioButtonGroupName]
  local length = EasyOptions:GetLength(radioButtons)
  if (length == 0) then
    return
  end

  local checkedRadioButton
  if (control:GetValue()) then
    checkedRadioButton = control
  else
    checkedRadioButton = EasyOptions:GetRadioButtonWithDefaultCheckedStateOfTrue(control)
  end

  local radioButton
  local isChecked
  local hasCheckedChanged
  local updateCheckedStateOfRadioButtonsInRadioButtonGroup = false
  for i = 1, length do
    radioButton = radioButtons[i]
    isChecked = (radioButton == checkedRadioButton)
    hasCheckedChanged = (radioButton:GetValue() ~= isChecked)
    EasyOptions:SetRadioButtonValue(radioButton, isChecked, updateCheckedStateOfRadioButtonsInRadioButtonGroup)
  end
end
