if not WeakAuras.IsLibsOK() then return end

local Type, Version = "WeakAurasTwoColumnDropdown", 6
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end

local secondLevelMt = {} -- Tag for our tables
local function CreateSecondLevelTable()
  local t = {}
  setmetatable(t, secondLevelMt)
  return t
end

local function IsSecondLevelTable(t)
  return getmetatable(t) == secondLevelMt
end

local function CompareValues(a, b)
  if type(a) == "table" and type(b) == "table" then
    for ak, av in pairs(a) do
      if b[ak] ~= av then
        return false
      end
    end
    for bk, bv in pairs(b) do
      if a[bk] ~= bv then
        return false
      end
    end
    return true
  else
    return a == b
  end
end

local methods = {
  ["DoLayout"] = function(self, mode)
    self.mode = mode
    if mode == "one" then
      self.firstDropdown.frame:Show()
      self.secondDropDown.frame:Hide()
      self.firstDropdown.frame:SetAllPoints(self.frame)
    else
      local halfWidth = self.frame:GetWidth() / 2
      self.firstDropdown.frame:Show()
      self.secondDropDown.frame:Show()
      self.firstDropdown.frame:ClearAllPoints()
      self.firstDropdown.frame:SetPoint("TOPLEFT", self.frame)
      self.firstDropdown.frame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMLEFT", halfWidth, 0)
      self.secondDropDown.frame:SetPoint("TOPLEFT", self.frame, halfWidth, 0)
      self.secondDropDown.frame:SetPoint("BOTTOMRIGHT", self.frame, "BOTTOMRIGHT")
    end
  end,
  ["OnAcquire"] = function(widget)
    local firstDropdown = AceGUI:Create("Dropdown")
    local secondDropDown = AceGUI:Create("Dropdown")

    firstDropdown:SetParent(widget)
    secondDropDown:SetParent(widget)
    firstDropdown:SetPulloutWidth(200)

    secondDropDown:SetLabel(" ")
    secondDropDown:SetPulloutWidth(200)
    secondDropDown.userdata.defaultSelection = {}

    widget.firstDropdown = firstDropdown
    widget.secondDropDown = secondDropDown

    local OnFirstDropdownValueChanged = function(self, event, value)
      local displayName = widget.userdata.firstList[value]
      local treeValue = widget.userdata.tree[displayName]
      if IsSecondLevelTable(treeValue) then
        local oldValue
        if widget.userdata.secondList then
          local v = widget.secondDropDown:GetValue()
          if v then
            oldValue = widget.userdata.secondList[v]
          end
        end
        local secondList = {}
        for displayName, _ in pairs(treeValue) do
          tinsert(secondList, displayName)
        end
        table.sort(secondList)

        local oldValueIndex = tIndexOf(secondList, oldValue)
        widget.userdata.secondList = secondList
        widget.secondDropDown:SetList(secondList)
        widget:DoLayout("two")

        if (oldValueIndex) then
          widget.secondDropDown:SetValue(oldValueIndex)

          local v = widget:GetValue()
          if (v) then
            widget:Fire("OnValueChanged", v)
          end
        else
          local default = widget.secondDropDown.userdata.defaultSelection[displayName]
          if default then
            local index = tIndexOf(secondList, default)
            widget.secondDropDown:SetValue(index)
            local v = widget:GetValue()
            if (v) then
              widget:Fire("OnValueChanged", v)
            end
          else
            widget.secondDropDown:SetValue()
          end
        end
      else
        widget.userdata.secondList = nil
        widget:DoLayout("one")
        widget:Fire("OnValueChanged", treeValue)
      end
    end

    firstDropdown.OnFirstDropdownValueChanged = OnFirstDropdownValueChanged

    local OnSecondDropdownValueChanged = function(self, event, value)
      widget:Fire("OnValueChanged", widget:GetValue())
    end

    firstDropdown:SetCallback("OnValueChanged", OnFirstDropdownValueChanged)
    secondDropDown:SetCallback("OnValueChanged", OnSecondDropdownValueChanged)

    local function FireOnEnter(self, event)
      widget:Fire("OnEnter")
    end

    local function FireOnLeave(self, event)
      widget:Fire("OnLeave")
    end

    firstDropdown:SetCallback("OnEnter", FireOnEnter)
    firstDropdown:SetCallback("OnLeave", FireOnLeave)
    secondDropDown:SetCallback("OnEnter", FireOnEnter)
    secondDropDown:SetCallback("OnLeave", FireOnLeave)


    widget:DoLayout("two")
  end,
  ["OnRelease"] = function(self)
    self.firstDropdown:SetCallback("OnValueChanged", nil)
    self.secondDropDown:SetCallback("OnValueChanged", nil)
    self.firstDropdown:SetCallback("OnEnter", nil)
    self.firstDropdown:SetCallback("OnLeave", nil)
    self.secondDropDown:SetCallback("OnEnter", nil)
    self.secondDropDown:SetCallback("OnLeave", nil)

    AceGUI:Release(self.firstDropdown)
    AceGUI:Release(self.secondDropDown)

    self.firstDropdown = nil
    self.secondDropDown = nil
  end,
  ["SetLabel"] = function(self, ...)
    self.firstDropdown:SetLabel(...)
  end,
  ["SetValue"] = function(self, value)
    for displayName, treeValue in pairs(self.userdata.tree) do
      if CompareValues(treeValue, value) then
        self:DoLayout("one")
        self.firstDropdown:SetValue(tIndexOf(self.userdata.firstList, displayName))
        return
      elseif IsSecondLevelTable(treeValue) then
        for displayName2, key in pairs(treeValue) do
          if CompareValues(key, value) then
            self:DoLayout("two")
            local index = tIndexOf(self.userdata.firstList, displayName);
            self.firstDropdown:SetValue(index)
            self.firstDropdown:OnFirstDropdownValueChanged("", index)
            self.secondDropDown:SetValue(tIndexOf(self.userdata.secondList, displayName2))
            return
          end
        end
      end
    end
    self:DoLayout("one")
    self.firstDropdown:SetValue(nil)
  end,
  ["GetValue"] = function(self)
    local displayName1 = self.userdata.firstList[self.firstDropdown:GetValue()]

    local treeValue = self.userdata.tree[displayName1]
    if not treeValue then
      return nil
    end
    if not IsSecondLevelTable(treeValue) then
      return treeValue
    end

    local displayName2 = self.userdata.secondList[self.secondDropDown:GetValue()]
    treeValue = treeValue[displayName2]
    if not treeValue then
      return nil
    end
    return treeValue
  end,
  ["SetList"] = function(self, list, order, itemType)
    local tree = {}
    for key, displayName in pairs(list) do
      if type(displayName) == "table" then
        local base = displayName[1]
        local suffix = displayName[2]
        tree[base] = tree[base] or CreateSecondLevelTable()
        tree[base][suffix] = key
        if displayName[3] == true then
          self.secondDropDown.userdata.defaultSelection[base] = suffix
        end
      else
        tree[displayName] = key
      end
    end
    self.userdata.tree = tree

    local firstList = {}
    for displayName, _ in pairs(tree) do
      tinsert(firstList, displayName)
    end
    table.sort(firstList)
    self.userdata.firstList = firstList
    self.firstDropdown:SetList(firstList, order, itemType)
  end,
  ["OnWidthSet"] = function(self)
    self:DoLayout(self.mode)
  end
}

local function Constructor()
  local frame = CreateFrame("Frame", nil, UIParent)
  frame:SetHeight(50)
  frame:SetWidth(50)

  local content = CreateFrame("Frame", nil, frame)
  content:SetPoint("TOPLEFT", 0, 0)
  content:SetPoint("BOTTOMRIGHT", 0, 0)

  local widget = {
    frame = frame,
    content = content,
    type = Type
  }
  for method, func in pairs(methods) do
    widget[method] = func
  end
  return AceGUI:RegisterAsWidget(widget)
end

AceGUI:RegisterWidgetType(Type, Constructor, Version)
