----------------------------
-- Configuration UI Tools --
----------------------------
local AddOnName, nsVars = ...
local ss = nsVars.SS

local CreateFrame, UIParent, getglobal, InterfaceOptions_AddCategory = CreateFrame, UIParent, getglobal, InterfaceOptions_AddCategory
local select, strlower, setmetatable, tonumber = select, strlower, setmetatable, tonumber
local UIDropDownMenu_Initialize, UIDropDownMenu_JustifyText = UIDropDownMenu_Initialize, UIDropDownMenu_JustifyText
local GetCursorPosition, UIDropDownMenu_SetWidth = GetCursorPosition, UIDropDownMenu_SetWidth

local itemCol = 1
local itemRow = 1

--- Local metatables.
-----------------------
local mtButton = {
  parent  = UIParent,
  width   = 135,
  height  = 25,
  anch    = "TOPLEFT",
  x       = 0,
  y       = 0,
  text    = "",
  onClick = function() end,
}

local mtCheck = {
  parent  = UIParent,
  anch    = "TOPLEFT",
  x       = 0,
  y       = 0,
  text    = "Show Value",
  checked = false,
  onClick = function() end,
}

local mtColorSelect = {
  parent        = UIParent,
  width         = 185,
  height        = 175,
  wheelWidth    = 120,
  barWidth      = 20,
  thumbWidth    = 14,
  anch          = "TOPLEFT",
  x             = 0,
  y             = 0,
}

local mtEdit = {
  parent    = UIParent,
  width     = 150,
  height    = 25,
  anch      = "TOPLEFT",
  x         = 0,
  y         = 0,
  letters   = 0,
  text      = "",
  autofocus = false,
}

local mtLabel = {
  parent   = UIParent,
  width    = 150,
  height   = 50,
  anch     = "TOPLEFT",
  x        = 0,
  y        = 0,
  text     = "",
  justifyV = "CENTER",
  justifyH = "CENTER",
  font     = ss.SharedMedia.fontChoices[1],
  fontSize = 19,
  color    = {r=1,g=1,b=1,a=1,},
}

--- Helper functions
----------------------
local getItemPos = function()
  if itemCol == 1 then
    itemCol = itemCol + 1
    
    if itemRow == 1 then
      return "TOPLEFT", 26, -181
    elseif itemRow == 2 then
      return "TOPLEFT", 26, -219
    elseif itemRow == 3 then
      return "TOPLEFT", 26, -257
    elseif itemRow == 4 then
      return "TOPLEFT", 26, -295
    elseif itemRow == 5 then
      return "TOPLEFT", 26, -335
    end
  elseif itemCol == 2 then
    local row = itemRow
    itemCol = itemCol - 1
    itemRow = itemRow + 1
    
    if row == 1 then
      return "TOPLEFT", 200, -181
    elseif row == 2 then
      return "TOPLEFT", 200, -219
    elseif row == 3 then
      return "TOPLEFT", 200, -257
    elseif row == 4 then
      return "TOPLEFT", 200, -295
    elseif row == 5 then
      return "TOPLEFT", 200, -335
    end
  end
  
  return "CENTER", 0, 0
end

local getCheckbox = function(name, text, parent, checked, onClick)
  parent[name] = CreateFrame("CheckButton",ss._f..name,parent, "UICheckButtonTemplate")
  parent[name]:SetPoint  (getItemPos())
  parent[name]:SetChecked(checked)
  
  getglobal(ss._f..name.."Text"):SetText(text)
  parent[name]:SetScript("OnClick", onClick)
end

local getDropdown = function(menu)
  local dropdown   = CreateFrame("Frame", ss._f..menu.name, menu.parent, "UIDropDownMenuTemplate")
  local anch, x, y = getItemPos()

  dropdown:SetPoint(anch, x - 15, y)
  UIDropDownMenu_SetWidth   (dropdown, 55)
  UIDropDownMenu_Initialize (dropdown, menu.init)
  UIDropDownMenu_JustifyText(dropdown, "LEFT")
  
  if menu.text then
    dropdown.label = dropdown:GetParent():CreateFontString(ss._f..menu.name.."Label", "ARTWORK", "GameFontNormalSmall")
    dropdown.label:SetPoint    (anch,x + 75,y)
    dropdown.label:SetHeight   (dropdown:GetHeight())
    dropdown.label:SetText     (menu.text)
    dropdown.label:SetJustifyV ("CENTER")
    dropdown.label:SetJustifyH ("LEFT")
  end
  
  return dropdown
end

local mtConfigTools = {
  itemHeight       = 30,
  itemPadLeft      = 50,
  itemPadCorner    = 10,
  itemPadTitle     = 15,
  topMargin        = -60,
  leftLabelMargin  = 145,
  bannerColor      = ss.ClassResourceData.classColor(ss.curClass),
  titleColor       = {r=0.5,g=1,b=0.3,a=1},
  headerItemColor  = {r=1.0,g=0.7,b=0.5,a=1},
  itemColor        = {r=0,g=0.5,b=1,a=1},
  createButton          = function(btn)
    btn = setmetatable(btn, { __index = mtButton });
    
    local button = CreateFrame("Button", ss._f..btn.name, btn.parent, "GameMenuButtonTemplate")
    button:SetSize  (btn.width, btn.height)
    button:SetPoint (btn.anch, btn.x, btn.y)
    button:SetText  (btn.text)
    button:SetScript("OnClick", btn.onClick)
    
    return button
  end,
  createInterfaceOption = function(opt)
    local option   = CreateFrame("Frame", AddOnName .. (opt.name or "") .. "InterfaceOption")
    option.name    = opt.name and opt.name or AddOnName
    option.parent  = opt.parent
    option.refresh = opt.refresh
    InterfaceOptions_AddCategory(option)
    
    return option
  end,
  createDropdown        = function(menu)
    local dropdown = CreateFrame("Button", ss._f..menu.name, menu.parent, "UIDropDownMenuTemplate")
    dropdown:SetSize (menu.width, menu.height)
    dropdown:SetPoint(menu.anchor, menu.x, menu.y)
    UIDropDownMenu_Initialize (dropdown, menu.init)
    UIDropDownMenu_JustifyText(dropdown, menu.tAlign)
    
    if menu.label then
      dropdown.label = ss.ConfigTools.addItemLabel(menu.label.name, dropdown, menu.label.text, menu.label.location, menu.label.color)
    end
    
    return dropdown
  end,
  createSliderField     = function(sliderConf, sliderEditBoxConf)
    local slider = CreateFrame("Slider",ss._f..sliderConf.name,sliderConf.parent, "OptionsSliderTemplate")
    slider:SetPoint (sliderConf.anchor, sliderConf.x, sliderConf.y)
    slider:SetSize  (sliderConf.len, sliderConf.width)
    slider:SetScript("OnValueChanged", sliderConf.onChanged)
    
    if sliderConf.values then
      getglobal(slider:GetName().."Text"):SetText(sliderConf.values.label)
      getglobal(slider:GetName().."High"):SetText(sliderConf.values.high)
      getglobal(slider:GetName().."Low") :SetText(sliderConf.values.low)
      
      slider:SetMinMaxValues(sliderConf.values.low, sliderConf.values.high)
      slider:SetValueStep   (sliderConf.values.step)
      slider:SetValue       (sliderConf.values.value)
    end
    
    -- Add the glow scale slider edit box
    local sliderEditBox = ss.ConfigTools.createEditBox{
      parent         = slider,
      name           = sliderConf.name.."EditBox",
      width          = 50,
      height         = 10,
      anch           = "BOTTOMRIGHT",
      x              = 0,
      y              = 35,
      letters        = sliderEditBoxConf.maxLetters,
      text           = slider:GetValue(),
      onEnterPressed = sliderEditBoxConf.onEnterPressed,
    }
    
    return slider, sliderEditBox
  end,
  createCheckbox        = function(check)
    check = setmetatable(check, { __index = mtCheck });
    
    local checkbox  = CreateFrame("CheckButton", ss._f..check.name, check.parent, "UICheckButtonTemplate")
    checkbox:SetPoint  (check.anch, check.x, check.y)
    checkbox:SetChecked(check.checked)
    getglobal(ss._f..check.name.."Text"):SetText(check.text)
    checkbox:SetScript("OnClick", check.onClick)
    
    return checkbox
  end,
  createEditBox         = function(edit)
    edit = setmetatable(edit, { __index = mtEdit });
    
    local editBox = CreateFrame("EditBox", ss._f..edit.name, edit.parent, "InputBoxTemplate")
    editBox:SetSize      (edit.width, edit.height)
    editBox:SetPoint     (edit.anch,edit.x,edit.y)
    editBox:SetMaxLetters(edit.letters)
    editBox:SetText      (edit.text or "")
    editBox:SetAutoFocus (edit.autofocus or false)
    
    if (edit.onEnterPressed) then
      editBox:SetScript("OnEnterPressed", edit.onEnterPressed)
    else
      editBox:SetScript("OnEnterPressed", function(self)
        self:ClearFocus()
      end)
    end
    
    if (edit.onTabPressed) then
      editBox:SetScript("OnTabPressed", edit.onTabPressed)
    end
  
    if (edit.onTextChanged) then
      editBox:SetScript("OnTextChanged", edit.onTextChanged)
    end
    
    if edit.label then
      editBox.label = ss.ConfigTools.addItemLabel(edit.label.name, editBox, edit.label.text, edit.label.location, edit.label.color)
    end
    
    return editBox
  end,
  createColorPicker     = function(colorSelect)
    colorSelect = setmetatable(colorSelect, { __index = mtColorSelect });
    
    -- Create the ColorSelect frame.
    local colorFields = CreateFrame("ColorSelect", ss._f.."ColorFieldContainer", colorSelect.parent)
    colorFields:SetPoint(colorSelect.anch, colorSelect.x, colorSelect.y)
    colorFields:SetSize (colorSelect.width, colorSelect.height)
    
    -- Create colorpicker wheel.
    colorFields.cWheelTexture = colorFields:CreateTexture()
    colorFields.cWheelTexture:SetWidth (colorSelect.wheelWidth)
    colorFields.cWheelTexture:SetHeight(colorSelect.wheelWidth)
    colorFields.cWheelTexture:SetPoint ("TOPLEFT",0,0)
    colorFields:SetColorWheelTexture   (colorFields.cWheelTexture)
    
    colorFields.cWheelThumbTexture = colorFields:CreateTexture()
    colorFields.cWheelThumbTexture:SetTexture ("Interface\\Buttons\\UI-ColorPicker-Buttons")
    colorFields.cWheelThumbTexture:SetWidth   (colorSelect.thumbWidth)
    colorFields.cWheelThumbTexture:SetHeight  (colorSelect.thumbWidth)
    colorFields.cWheelThumbTexture:SetTexCoord(0,0.15625, 0, 0.625)
    colorFields:SetColorWheelThumbTexture     (colorFields.cWheelThumbTexture)
    
    -- Create the colorpicker slider.
    colorFields.valueTexture = colorFields:CreateTexture()
    colorFields.valueTexture:SetWidth (colorSelect.barWidth)
    colorFields.valueTexture:SetHeight(colorSelect.wheelWidth)
    colorFields.valueTexture:SetPoint ("TOPRIGHT", (colorSelect.barWidth * -1) - 10,0)
    colorFields:SetColorValueTexture  (colorFields.valueTexture)
    
    colorFields.valueThumbTexture = colorFields:CreateTexture()
    colorFields.valueThumbTexture:SetTexture ("Interface\\Buttons\\UI-ColorPicker-Buttons")
    colorFields.valueThumbTexture:SetWidth   (colorSelect.barWidth)
    colorFields.valueThumbTexture:SetHeight  (colorSelect.thumbWidth)
    colorFields.valueThumbTexture:SetTexCoord(0.25, 1, 0.875, 0)
    colorFields:SetColorValueThumbTexture    (colorFields.valueThumbTexture)
    
    -- Create the alpha widget.
    colorFields.alphaTextureContainer = CreateFrame("Frame", ss._f.."ColorFieldAlphaContainer", colorFields)
    colorFields.alphaTextureContainer:SetHeight      (colorSelect.wheelWidth)
    colorFields.alphaTextureContainer:SetWidth       (colorSelect.barWidth)
    colorFields.alphaTextureContainer:SetPoint       ("TOPRIGHT",0,0)
    colorFields.alphaTextureContainer:RegisterForDrag("LeftButton")
    colorFields.alphaTextureContainer:EnableMouse    (true)
    
    -- TODO: Set a background that is visible for transparent colors.
    
    colorFields:SetScript("OnColorSelect", function(self)
      -- Ensure custom fields are updated.
      local r, g, b, a = self:GetColorRGBA()
      self.colorFieldA :SetText(100 * a)
      self.colorFieldR :SetText(r * 255)
      self.colorFieldG :SetText(g * 255)
      self.colorFieldB :SetText(b * 255)
      self.alphaTexture:SetGradientAlpha("VERTICAL",0,0,0,1,r,g,b,1)
    end)
    
    function colorFields:SetColorRGBA(r, g, b, a)
      self:SetColorAlpha(a)
      self:SetColorRGB(r,g,b)
    end
    
    function colorFields:GetColorRGBA()
      local r, g, b = self:GetColorRGB()
      return r, g, b, self:GetColorAlpha()
    end
    
    function colorFields:SetColorAlpha(a)
      a = a < 0 and 0 or a
      a = a > 1 and 1 or a
      
      -- Update Alpha slider and trigger OnColorSelect.
      self.alphaThumbTexture:SetPoint("TOPRIGHT",0,7 - (120 - 120 * a))
      self.colorFieldA:SetText(100 * a)
      self:SetColorRGB(self:GetColorRGB())
    end
    
    function colorFields:GetColorAlpha()
      local a = tonumber(self.colorFieldA:GetText()) or 100
      return a/100
    end
    
    colorFields.alphaTextureContainer:SetScript("OnDragStart", function(self)
      colorFields.alphaThumbContainer.minY = self:GetBottom()
    end)
    colorFields.alphaTextureContainer:SetScript("OnDragStop", function(self)
      colorFields.alphaThumbContainer.minY = nil
    end)
  
    colorFields.alphaThumbTexture = colorFields.alphaTextureContainer:CreateTexture()
    colorFields.alphaThumbTexture:SetTexture ("Interface\\Buttons\\UI-ColorPicker-Buttons")
    colorFields.alphaThumbTexture:SetHeight  (colorSelect.thumbWidth)
    colorFields.alphaThumbTexture:SetWidth   (colorSelect.barWidth)
    colorFields.alphaThumbTexture:SetPoint   ("TOPRIGHT",0,colorSelect.thumbWidth/2)
    colorFields.alphaThumbTexture:SetTexCoord(0.275, 1, 0.875, 0)
    
    colorFields.alphaTexture = colorFields.alphaTextureContainer:CreateTexture()
    colorFields.alphaTexture:SetAllPoints    (colorFields.alphaTextureContainer)
    colorFields.alphaTexture:SetColorTexture (1,1,1,1)
    colorFields.alphaTexture:SetGradientAlpha("VERTICAL",0,0,0,0,1,1,1,1)
    
    -- Add the alpha slider.
    colorFields.alphaThumbContainer = CreateFrame("Frame", ss._f.."AlphaDragFrame", colorFields)
    
    colorFields.alphaThumbContainer:SetScript("OnUpdate", function(self)
      if self.minY then
        -- Calculate yPos relative to container bottom.
        local _,yPos = GetCursorPosition()
        yPos = yPos/UIParent:GetScale() - self.minY
        
        -- Ensure movement is within the container boundaries.
        yPos = (yPos < 0)   and 0   or yPos
        yPos = (yPos > 120) and 120 or yPos
        yPos = yPos + colorSelect.thumbWidth/2
        
        -- Drag the slider.
        colorFields.alphaThumbTexture:SetPoint("TOPRIGHT",0,yPos - colorSelect.wheelWidth)
        
        -- Update the alpha value.
        yPos = (yPos - colorSelect.thumbWidth/2) / 120
        colorFields:SetColorAlpha(yPos)
      end
    end)
    
    -- Create the edit fields
    local height = 25
    local width  = 30
    local xPos   = -width - 10
    local yPos   = height - 5
    
    -- Create the R color field
    colorFields.colorFieldR = ss.ConfigTools.createEditBox{
      parent        = colorFields,
      name          = "ColorFieldR",
      width         = width,
      height        = height,
      anch          = "BOTTOMRIGHT",
      x             = (xPos*3),
      y             = 0,
      letters       = 3,
      onTextChanged = function(self)
        local val = self:GetText()
        if tonumber(val) then
          local r,g,b = ss.ConfigPreview.colorFields:GetColorRGB()
          colorFields:SetColorRGB(val/255,g,b)
        end
        
        if colorSelect.onTextChanged then
          colorSelect.onTextChanged(self)
        end
      end,
      onTabPressed = function(self)
        if colorFields.colorFieldG then
          colorFields.colorFieldG:SetFocus()
        else
          self:ClearFocus()
        end
      end,
    }
    
    -- Create the R color label
    colorFields.colorFieldR.label = ss.ConfigTools.createLabel{
      parent = colorFields,
      name   = "ColorFieldRLabel",
      width  = width,
      height = height,
      anch   = "BOTTOMRIGHT",
      x      = (xPos*3) - 10,
      y      = yPos,
      color  = ss.ConfigTools.itemColor,
      text   = "R",
    }
    
    -- Create the G color field
    colorFields.colorFieldG = ss.ConfigTools.createEditBox{
      parent        = colorFields,
      name          = "ColorFieldG",
      width         = width,
      height        = height,
      anch          = "BOTTOMRIGHT",
      x             = (xPos*2),
      y             = 0,
      letters       = 3,
      onTextChanged = function(self)
        local val = self:GetText()
        if tonumber(val) then
          local r,g,b = ss.ConfigPreview.colorFields:GetColorRGB()
          colorFields:SetColorRGB(r,val/255,b)
        end
        
        if colorSelect.onTextChanged then
          colorSelect.onTextChanged(self)
        end
      end,
      onTabPressed = function(self)
        if colorFields.colorFieldB then
          colorFields.colorFieldB:SetFocus()
        else
          self:ClearFocus()
        end
      end,
    }
    
    -- Create the G color label
    colorFields.colorFieldG.label = ss.ConfigTools.createLabel{
      parent = colorFields,
      name   = "ColorFieldGLabel",
      width  = width,
      height = height,
      anch   = "BOTTOMRIGHT",
      x      = (xPos*2) - 10,
      y      = yPos,
      color  = ss.ConfigTools.itemColor,
      text   = "G",
    }
    
    -- Create the B color field
    colorFields.colorFieldB = ss.ConfigTools.createEditBox{
      parent        = colorFields,
      name          = "ColorFieldB",
      width         = width,
      height        = height,
      anch          = "BOTTOMRIGHT",
      x             = xPos,
      y             = 0,
      letters       = 3,
      onTextChanged = function(self)
        local val = self:GetText()
        if tonumber(val) then
          local r,g,b = ss.ConfigPreview.colorFields:GetColorRGB()
          colorFields:SetColorRGB(r,g,val/255)
        end
        
        if colorSelect.onTextChanged then
          colorSelect.onTextChanged(self)
        end
      end,
      onTabPressed = function(self)
        if colorFields.colorFieldA then
          colorFields.colorFieldA:SetFocus()
        else
          self:ClearFocus()
        end
      end,
    }
    
    -- Create the B color label
    colorFields.colorFieldB.label = ss.ConfigTools.createLabel{
      parent = colorFields,
      name   = "ColorFieldBLabel",
      width  = width,
      height = height,
      anch   = "BOTTOMRIGHT",
      x      = xPos - 10,
      y      = yPos,
      color  = ss.ConfigTools.itemColor,
      text   = "B",
    }
    
    -- Create the Alpha field
    colorFields.colorFieldA = ss.ConfigTools.createEditBox{
      parent        = colorFields,
      name          = "ColorFieldA",
      width         = width,
      height        = height,
      anch          = "BOTTOMRIGHT",
      x             = 0,
      y             = 0,
      letters       = 3,
      onTextChanged = function(self)
        local val = self:GetText()
        if tonumber(val) then
          colorFields:SetColorAlpha(val/100)
        end
        
        if colorSelect.onTextChanged then
          colorSelect.onTextChanged(self)
        end
      end,
      onTabPressed = function(self)
        self:ClearFocus()
      end,
    }
    
    -- Create the Alpha label
    colorFields.colorFieldA.label = ss.ConfigTools.createLabel{
      parent = colorFields,
      name   = "ColorFieldALabel",
      width  = width,
      height = height,
      anch   = "BOTTOMRIGHT",
      x      = -10,
      y      = yPos,
      color  = ss.ConfigTools.itemColor,
      text   = "A",
    }
    
    -- Add the class button
    colorFields.classButton = ss.ConfigTools.createButton{
      parent  = colorFields,
      name    = "ColorFieldClassButton",
      width   = 40,
      height  = height,
      anch    = "BOTTOMLEFT",
      x       = -15,
      y       = 0,
      text    = "Class",
      onClick = function()
        colorFields:SetColorRGBA(ss.ConfigTools.bannerColor.r, ss.ConfigTools.bannerColor.g, ss.ConfigTools.bannerColor.b, ss.ConfigTools.bannerColor.a)
      end,
    }
    
    return colorFields
  end,
  createLabel           = function(label)
    label = setmetatable(label, { __index = mtLabel });
    
    local fontString = label.parent:CreateFontString(label.name)
    fontString:SetFont     (ss.private.font["general"], 19)
    fontString:SetPoint    (label.anch, label.x, label.y)
    fontString:SetSize     (label.width, label.height)
    fontString:SetTextColor(label.color.r, label.color.g, label.color.b, label.color.a)
    fontString:SetText     (label.text)
    fontString:SetJustifyV (label.justifyV)
    fontString:SetJustifyH (label.justifyH)
    
    return fontString
  end,
  createTitle           = function(text, parent)
    local title = parent:CreateFontString(nil, "BossEmoteNormalHuge")
    title:SetFont     (ss.private.font["general"], 26)
    title:SetPoint    ("TOPRIGHT", -ss.ConfigTools.itemPadTitle, (ss.ConfigTools.topMargin - 3))
    title:SetSize     (400, 39)
    title:SetTextColor(ss.ConfigTools.titleColor.r, ss.ConfigTools.titleColor.g, ss.ConfigTools.titleColor.b, ss.ConfigTools.titleColor.a)
    title:SetText     (text)
    title:SetJustifyV ("TOP")
    title:SetJustifyH ("RIGHT")
    
    return title
  end,
  addItemLabel          = function(name, target, text, location, color)
    location = location or "left"
    color    = color    or ss.ConfigTools.itemColor
    
    -- Determine relative location
    local anch, x, y = select(3, target:GetPoint())
    x                = (strlower(location) == "left") and (x - ss.ConfigTools.leftLabelMargin) or ((strlower(location) == "top") and (x + 20) or x)
    y                = (strlower(location) == "left") and (y - 5)                              or ((strlower(location) == "top") and (y + 15) or y)
    
    local fontString = target:GetParent():CreateFontString(name)
    fontString:SetFont     (ss.private.font["general"], 19)
    fontString:SetPoint    (anch,x,y)
    fontString:SetSize     (181, 39)
    fontString:SetTextColor(color.r, color.g, color.b, color.a)
    fontString:SetText     (text)
    fontString:SetJustifyV ("TOP")
    fontString:SetJustifyH ("Left")
    
    return fontString
  end,
  getWidget             = function(parent, props)
    if props.type == "Checkbox" then
      getCheckbox(props.name, props.text, parent, props.checked, props.onClick)
    elseif props.type == "Dropdown" then
      parent[props.name] = getDropdown{
        name   = props.name,
        parent = parent,
        init   = props.init,
        text = props.text,
      };
    end
  end,
  getWidgetContainer    = function(parent, name)
    -- Add the widget container
    local widgetFrame = CreateFrame("Frame", ss._f..name, parent)
    widgetFrame:SetAllPoints(parent)
    
    -- Add the banner.
    widgetFrame.banner = widgetFrame:CreateFontString(name.."Banner", "ARTWORK", "NumberFontNormalHuge")
    widgetFrame.banner:SetFont     (ss.private.font["general"], 42)
    widgetFrame.banner:SetPoint    ("TOPRIGHT", ss.ConfigTools.itemPadTitle, -ss.ConfigTools.itemPadTitle)
    widgetFrame.banner:SetSize     (262, 57)
    widgetFrame.banner:SetTextColor(ss.ConfigTools.bannerColor.r, ss.ConfigTools.bannerColor.g, ss.ConfigTools.bannerColor.b, ss.ConfigTools.bannerColor.a)
    widgetFrame.banner:SetText     ("Seamore Spheres")
    widgetFrame.banner:SetJustifyV ("TOP")
    
    return widgetFrame
  end,
};

ss["ConfigTools"] = setmetatable({}, { __index = mtConfigTools });
