if not WeakAuras.IsCorrectVersion() then return end

local L = WeakAuras.L

local selfPoints = {
  default = "CENTER",
  RIGHT = function(data)
    if data.align  == "LEFT" then
      return "TOPLEFT"
    elseif data.align == "RIGHT" then
      return "BOTTOMLEFT"
    else
      return "LEFT"
    end
  end,
  LEFT = function(data)
    if data.align  == "LEFT" then
      return "TOPRIGHT"
    elseif data.align == "RIGHT" then
      return "BOTTOMRIGHT"
    else
      return "RIGHT"
    end
  end,
  UP = function(data)
    if data.align == "LEFT" then
      return "BOTTOMLEFT"
    elseif data.align == "RIGHT" then
      return "BOTTOMRIGHT"
    else
      return "BOTTOM"
    end
  end,
  DOWN = function(data)
    if data.align == "LEFT" then
      return "TOPLEFT"
    elseif data.align == "RIGHT" then
      return "TOPRIGHT"
    else
      return "TOP"
    end
  end,
  HORIZONTAL = function(data)
    if data.align == "LEFT" then
      return "TOP"
    elseif data.align == "RIGHT" then
      return "BOTTOM"
    else
      return "CENTER"
    end
  end,
  VERTICAL = function(data)
    if data.align == "LEFT" then
      return "LEFT"
    elseif data.align == "RIGHT" then
      return "RIGHT"
    else
      return "CENTER"
    end
  end,
  CIRCLE = "CENTER",
  COUNTERCIRCLE = "CENTER",
}

local gridSelfPoints = {
  RU = "BOTTOMLEFT",
  UR = "BOTTOMLEFT",
  LU = "BOTTOMRIGHT",
  UL = "BOTTOMRIGHT",
  RD = "TOPLEFT",
  DR = "TOPLEFT",
  LD = "TOPRIGHT",
  DL = "TOPRIGHT",
}

local function createOptions(id, data)
  local options = {
    __title = L["Dynamic Group Settings"],
    __order = 1,
    groupIcon = {
      type = "input",
      width = WeakAuras.normalWidth,
      name = WeakAuras.newFeatureString..L["Group Icon"],
      desc = L["Set Thumbnail Icon"],
      order = 0.5,
      get = function()
        return data.groupIcon and tostring(data.groupIcon) or ""
      end,
      set = function(info, v)
        data.groupIcon = v
        WeakAuras.Add(data)
        WeakAuras.UpdateThumbnail(data)
        WeakAuras.SetIconNames(data)
      end
    },
    chooseIcon = {
      type = "execute",
      width = WeakAuras.normalWidth,
      name = L["Choose"],
      order = 0.51,
      func = function() WeakAuras.OpenIconPicker(data, "groupIcon", true) end
    },
    -- grow options
    grow = {
      type = "select",
      width = WeakAuras.doubleWidth,
      name = L["Grow"],
      order = 1,
      values = WeakAuras.grow_types,
      set = function(info, v)
        data.grow = v
        local selfPoint = selfPoints[data.grow] or selfPoints.default
        if type(selfPoint) == "function" then
          selfPoint = selfPoint(data)
        end
        data.selfPoint = selfPoint
        WeakAuras.Add(data)
        WeakAuras.ReloadTriggerOptions(data)
        WeakAuras.ResetMoverSizer()
      end,
    },
    useAnchorPerUnit = {
      type = "toggle",
      order = 1.5,
      width = WeakAuras.normalWidth,
      name = WeakAuras.newFeatureString..L["Group by Frame"],
      desc = L[
[[Group and anchor each auras by frame.

- Nameplates: attach to nameplates per unit.
- Unit Frames: attach to unit frame buttons per unit.
- Custom Frames: choose which frame each region should be anchored to.]]
      ],
      hidden = function() return data.grow == "CUSTOM" end,
    },
    anchorPerUnit = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = WeakAuras.newFeatureString..L["Group by Frame"],
      order = 1.6,
      values = {
        ["UNITFRAME"] = L["Unit Frames"],
        ["NAMEPLATE"] = L["Nameplates"],
        ["CUSTOM"] = L["Custom Frames"],
      },
      hidden = function() return data.grow == "CUSTOM" end,
      disabled = function() return not data.useAnchorPerUnit end
    },
    -- custom grow option added below
    align = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Align"],
      order = 2,
      values = WeakAuras.align_types,
      set = function(info, v)
        data.align = v
        local selfPoint = selfPoints[data.grow] or selfPoints.default
        if type(selfPoint) == "function" then
          selfPoint = selfPoint(data)
        end
        data.selfPoint = selfPoint
        WeakAuras.Add(data)
        WeakAuras.ReloadTriggerOptions(data)
        WeakAuras.ResetMoverSizer()
      end,
      hidden = function() return (data.grow == "CUSTOM" or data.grow == "LEFT" or data.grow == "RIGHT" or data.grow == "HORIZONTAL" or data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE" or data.grow == "GRID") end,
      disabled = function() return data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE" end
    },
    rotated_align = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Align"],
      order = 3,
      values = WeakAuras.rotated_align_types,
      hidden = function() return (data.grow == "CUSTOM" or data.grow == "UP" or data.grow == "DOWN" or data.grow == "VERTICAL" or data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE" or data.grow == "GRID") end,
      get = function() return data.align; end,
      set = function(info, v)
        data.align = v
        local selfPoint = selfPoints[data.grow] or selfPoints.default
        if type(selfPoint) == "function" then
          selfPoint = selfPoint(data)
        end
        data.selfPoint = selfPoint
        WeakAuras.Add(data)
        WeakAuras.ReloadTriggerOptions(data)
        WeakAuras.ResetMoverSizer()
      end,
    },
    -- circle grow options
    constantFactor = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Constant Factor"],
      order = 4,
      values = WeakAuras.circular_group_constant_factor_types,
      hidden = function() return data.grow ~= "CIRCLE" and data.grow ~= "COUNTERCIRCLE" end
    },
    rotation = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Start Angle"],
      order = 5,
      min = 0,
      max = 360,
      bigStep = 3,
      hidden = function() return data.grow ~= "CIRCLE" and data.grow ~= "COUNTERCIRCLE" end
    },
    arcLength = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Arc Length"],
      order = 7,
      min = 0,
      max = 360,
      bigStep = 3,
      hidden = function() return data.grow ~= "CIRCLE" and data.grow ~= "COUNTERCIRCLE" end
    },
    radius = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Radius"],
      order = 6,
      softMin = 0,
      softMax = 500,
      bigStep = 1,
      hidden = function() return data.grow == "CUSTOM" or not((data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE") and data.constantFactor == "RADIUS") end
    },
    -- grid grow options
    gridType = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Grid direction"],
      order = 8,
      values = WeakAuras.grid_types,
      hidden = function() return data.grow ~= "GRID" end,
      set = function(info, value)
        data.selfPoint = gridSelfPoints[value]
        data.gridType = value
        WeakAuras.Add(data)
        WeakAuras.ResetMoverSizer()
      end,
    },
    gridWidth = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = function()
        if not data.gridType then return "" end
        if data.gridType:find("^[RL]") then
          return L["Row Width"]
        else
          return L["Column Height"]
        end
      end,
      order = 9,
      min = 1,
      softMax = 20,
      step = 1,
      hidden = function() return data.grow ~= "GRID" end,
    },
    rowSpace = {
      type = "range",
      name = L["Row Space"],
      width = WeakAuras.normalWidth,
      order = 10,
      softMin = 0,
      softMax = 300,
      step = 1,
      hidden = function() return data.grow ~= "GRID" end,
    },
    columnSpace = {
      type = "range",
      name = L["Column Space"],
      width = WeakAuras.normalWidth,
      order = 11,
      softMin = 0,
      softMax = 300,
      step = 1,
      hidden = function() return data.grow ~= "GRID" end,
    },
    -- generic grow options
    space = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Space"],
      order = 7,
      softMin = 0,
      softMax = 300,
      bigStep = 1,
      hidden = function()
        return data.grow == "CUSTOM"
            or data.grow == "GRID"
            or ((data.grow == "CIRCLE" or data.grow == "COUNTERCIRCLE") and data.constantFactor == "RADIUS")
      end
    },
    stagger = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Stagger"],
      order = 8,
      min = -50,
      max = 50,
      step = 0.1,
      bigStep = 1,
      hidden = function()
        return data.grow == "CUSTOM"
            or data.grow == "CIRCLE"
            or data.grow == "COUNTERCIRCLE"
            or data.grow == "GRID"
      end
    },
    -- sort options
    sort = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Sort"],
      order = 20,
      values = WeakAuras.group_sort_types
    },
    -- custom sort option added below
    hybridPosition = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Hybrid Position"],
      order = 21,
      values = WeakAuras.group_hybrid_position_types,
      hidden = function() return not(data.sort == "hybrid") end,
    },
    hybridSortMode = {
      type = "select",
      width = WeakAuras.normalWidth,
      name = L["Hybrid Sort Mode"],
      order = 22,
      values = WeakAuras.group_hybrid_sort_types,
      hidden = function() return not(data.sort == "hybrid") end,
    },
    sortHybrid = {
      type = "multiselect",
      width = "full",
      name = L["Select the auras you always want to be listed first"],
      order = 23,
      hidden = function() return not(data.sort == "hybrid") end,
      values = function()
        return data.controlledChildren
      end,
      get = function(info, index)
        local id = data.controlledChildren[index]
        return data.sortHybridTable and data.sortHybridTable[id] or false;
      end,
      set = function(info, index)
        if not data.sortHybridTable then data.sortHybridTable = {}; end
        local id = data.controlledChildren[index]
        local cur = data.sortHybridTable and data.sortHybridTable[id] or false;
        data.sortHybridTable[id] = not(cur);
      end,
    },
    sortSpace = {
      type = "description",
      name = "",
      width = WeakAuras.doubleWidth,
      order = 24,
      hidden = function() return data.sort == "hybrid" end
    },
    useLimit = {
      type = "toggle",
      order = 25,
      width = WeakAuras.normalWidth,
      name = L["Limit"],
      hidden = function() return data.grow == "CUSTOM" end,
    },
    limit = {
      type = "range",
      order = 26,
      width = WeakAuras.normalWidth,
      name = L["Limit"],
      min = 0,
      softMax = 20,
      step = 1,
      disabled = function() return not data.useLimit end,
      hidden = function() return data.grow == "CUSTOM" end,
    },
    animate = {
      type = "toggle",
      width = WeakAuras.normalWidth,
      name = L["Animated Expand and Collapse"],
      order = 27
    },
    scale = {
      type = "range",
      width = WeakAuras.normalWidth,
      name = L["Group Scale"],
      order = 28,
      min = 0.05,
      softMax = 2,
      bigStep = 0.05,
      get = function()
        return data.scale or 1
      end,
      set = function(info, v)
        data.scale = data.scale or 1
        local change = 1 - (v/data.scale)
        data.xOffset = data.xOffset/(1-change)
        data.yOffset = data.yOffset/(1-change)
        data.scale = v
        WeakAuras.Add(data);
        WeakAuras.ResetMoverSizer();
      end
    },
    endHeader = {
      type = "header",
      order = 100,
      name = "",
    },
  };

  WeakAuras.AddCodeOption(options, data, L["Custom Grow"], "custom_grow", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Dynamic-Group",
                          2, function() return data.grow ~= "CUSTOM" end, {"customGrow"}, nil, nil, nil, nil, nil, true)
  WeakAuras.AddCodeOption(options, data, L["Custom Sort"], "custom_sort", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Dynamic-Group",
                          21, function() return data.sort ~= "custom" end, {"customSort"}, nil, nil, nil, nil, nil, true)
  WeakAuras.AddCodeOption(options, data, L["Custom Anchor"], "custom_anchor_per_unit", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Dynamic-Group",
                          1.7, function() return not(data.grow ~= "CUSTOM" and data.useAnchorPerUnit and data.anchorPerUnit == "CUSTOM") end, {"customAnchorPerUnit"}, nil, nil, nil, nil, nil, true)

  local borderHideFunc = function() return data.useAnchorPerUnit or data.grow == "CUSTOM" end
  local disableSelfPoint = function() return data.grow ~= "CUSTOM" and data.grow ~= "GRID" and not data.useAnchorPerUnit end

  for k, v in pairs(WeakAuras.BorderOptions(id, data, nil, borderHideFunc, 70)) do
    options[k] = v
  end

  return {
    dynamicgroup = options,
    position = WeakAuras.PositionOptions(id, data, nil, true, disableSelfPoint),
  };
end

local function createThumbnail()
  -- frame
  local thumbnail = CreateFrame("FRAME", nil, UIParent);
  thumbnail:SetWidth(32);
  thumbnail:SetHeight(32);

  -- border
  local border = thumbnail:CreateTexture(nil, "OVERLAY");
  border:SetAllPoints(thumbnail);
  border:SetTexture("Interface\\BUTTONS\\UI-Quickslot2.blp");
  border:SetTexCoord(0.2, 0.8, 0.2, 0.8);

  return thumbnail
end

local function defaultIconAnimation(self, elapsed)
  self.elapsed = self.elapsed + elapsed
  if(self.elapsed < 0.5) then
    self.t2:SetPoint("TOP", self.t1, "BOTTOM", 0, -2 + (28 * self.elapsed))
    self.t2:SetAlpha(1 - (2 * self.elapsed))
  elseif(self.elapsed < 1.5) then
  -- do nothing
  elseif(self.elapsed < 2) then
    self.t2:SetPoint("TOP", self.t1, "BOTTOM", 0, -2 + (28 * (2 - self.elapsed)))
    self.t2:SetAlpha((2 * self.elapsed) - 3)
  elseif(self.elapsed < 3) then
  -- do nothing
  else
    self.elapsed = self.elapsed - 3
  end
end

local function createAnimatedDefaultIcon(parent)
  local defaultIcon = CreateFrame("FRAME", nil, parent);
  parent.defaultIcon = defaultIcon;

  local t1 = defaultIcon:CreateTexture(nil, "ARTWORK");
  t1:SetWidth(24);
  t1:SetHeight(6);
  t1:SetColorTexture(0.8, 0, 0);
  t1:SetPoint("TOP", parent, "TOP", 0, -6);
  local t2 = defaultIcon:CreateTexture(nil, "ARTWORK");
  t2:SetWidth(12);
  t2:SetHeight(12);
  t2:SetColorTexture(0.2, 0.8, 0.2);
  t2:SetPoint("TOP", t1, "BOTTOM", 0, -2);
  local t3 = defaultIcon:CreateTexture(nil, "ARTWORK");
  t3:SetWidth(30);
  t3:SetHeight(4);
  t3:SetColorTexture(0.1, 0.25, 1);
  t3:SetPoint("TOP", t2, "BOTTOM", 0, -2);
  local t4 = defaultIcon:CreateTexture(nil, "OVERLAY");
  t4:SetWidth(1);
  t4:SetHeight(36);
  t4:SetColorTexture(1, 1, 1);
  t4:SetPoint("CENTER", parent, "CENTER");

  defaultIcon.t1 = t1
  defaultIcon.t2 = t2

  defaultIcon.elapsed = 0;
  defaultIcon:SetScript("OnUpdate", defaultIconAnimation)
  defaultIcon:SetScript("OnHide", function(self) self:SetScript("OnUpdate", nil) end)
  defaultIcon:SetScript("OnShow", function(self) self:SetScript("OnUpdate", defaultIconAnimation) end)

  return defaultIcon
end

-- Modify preview thumbnail
local function modifyThumbnail(parent, frame, data)
  function frame:SetIcon(path)
    if not frame.icon then
      local icon = frame:CreateTexture(nil, "OVERLAY")
      icon:SetAllPoints(frame)
      frame.icon = icon
    end
    local success = frame.icon:SetTexture(path or data.groupIcon) and (path or data.groupIcon)
    if success then
      if frame.defaultIcon then
        frame.defaultIcon:Hide()
      end
      frame.icon:Show()
    else
      if frame.icon then
        frame.icon:Hide()
      end
      if not frame.defaultIcon then
        frame.defaultIcon = createAnimatedDefaultIcon(frame)
      end
      frame.defaultIcon:Show()
    end
  end
  frame:SetIcon()
end

local function createIcon()
  local thumbnail = createThumbnail(UIParent)
  thumbnail.defaultIcon = createAnimatedDefaultIcon(thumbnail)
  return thumbnail
end

WeakAuras.RegisterRegionOptions("dynamicgroup", createOptions, createIcon, L["Dynamic Group"], createThumbnail, modifyThumbnail, L["A group that dynamically controls the positioning of its children"]);
