local tablex = require("core.tablex")
local frame = require("ui.frame")
local frameNode = require("ui.frameNode")
local frameConsts = require("ui.frameConsts")
local FrameGroup = {}
setmetatable(FrameGroup, {
  __index = frameNode.FrameNode
})
function FrameGroup.New()
  local newTable = {
    _rootNode = nil,
    _nodes = {},
    _framePadding = {},
    _padding = 0.5,
    _borderPadding = 2.75,
    _frameAnchorVertical = frameConsts.ANCHOR.Top,
    _frameAnchorHorizontal = frameConsts.ANCHOR.Left,
    _frameOrientation = frameConsts.ORIENTATION.Vertical,
    _frameDirection = frameConsts.DIRECTION.Down,
    _reverseBackdropScaleDirection = false,
    _anchoredFrames = {},
    _directionScalar = 1,
    _addLengthFirst = false
  }
  local mt = {__index = FrameGroup}
  setmetatable(newTable, mt)
  newTable:SetOrientation(newTable._frameOrientation)
  newTable:SetDirection(newTable._frameDirection)
  return newTable
end
function FrameGroup:SetOrientation(orientation)
  self._frameOrientation = orientation
  self:UpdateOrientationAndDirection()
end
function FrameGroup:SetDirection(direction)
  assert(direction == frameConsts.DIRECTION.Up or direction == frameConsts.DIRECTION.Down or direction == frameConsts.DIRECTION.Left or direction == frameConsts.DIRECTION.Right, "Incorrect value passed into SetDirection in FrameGroup")
  assert(self._frameOrientation == frameConsts.ORIENTATION.Vertical and (direction == frameConsts.DIRECTION.Up or direction == frameConsts.DIRECTION.Down) or self._frameOrientation == frameConsts.ORIENTATION.Horizontal and (direction == frameConsts.DIRECTION.Left or direction == frameConsts.DIRECTION.Right), "Incorrect direction set given current orientation. If this wasn't an error, then set the orientation FIRST.")
  self._frameDirection = direction
  self:UpdateOrientationAndDirection()
end
function FrameGroup:UpdateOrientationAndDirection()
  if self._frameOrientation == frameConsts.ORIENTATION.Vertical and self._frameDirection == frameConsts.DIRECTION.Up or self._frameOrientation == frameConsts.ORIENTATION.Horizontal and self._frameDirection == frameConsts.DIRECTION.Right then
    self._directionScalar = 1
  else
    self._directionScalar = -1
  end
  self._addLengthFirst = false
  self._addLengthFirst = self._frameOrientation == frameConsts.ORIENTATION.Vertical and self._frameAnchorVertical == frameConsts.ANCHOR.Top and self._frameDirection == frameConsts.DIRECTION.Up or self._frameAnchorVertical == frameConsts.ANCHOR.Bottom and self._frameDirection == frameConsts.DIRECTION.Down or self._frameOrientation == frameConsts.ORIENTATION.Horizontal and self._frameAnchorVertical == frameConsts.ANCHOR.Right and self._frameDirection == frameConsts.DIRECTION.Right or self._frameAnchorVertical == frameConsts.ANCHOR.Left and self._frameDirection == frameConsts.DIRECTION.Left
end
function FrameGroup:Update()
  local initialY = 0
  local currentY = self._borderPadding * self._directionScalar
  if self._addLengthFirst then
    currentY = IncrementUpdateIterator(currentY, self._rootNode, self._frameOrientation, self._directionScalar)
    SetNewPositionScalar(self._rootNode, currentY, self._frameOrientation)
  else
    SetNewPositionScalar(self._rootNode, currentY, self._frameOrientation)
    currentY = IncrementUpdateIterator(currentY, self._rootNode, self._frameOrientation, self._directionScalar)
  end
  for _, currentNode in pairs(self._nodes) do
    if currentNode:IsActive() then
      if currentNode:GetHeight() ~= 0 then
        currentY = currentY + self._padding * self._directionScalar
      end
      if self._addLengthFirst then
        currentY = IncrementUpdateIterator(currentY, currentNode, self._frameOrientation, self._directionScalar)
        SetNewPositionScalar(currentNode, currentY, self._frameOrientation)
      else
        SetNewPositionScalar(currentNode, currentY, self._frameOrientation)
        currentY = IncrementUpdateIterator(currentY, currentNode, self._frameOrientation, self._directionScalar)
      end
    end
  end
  currentY = currentY + self._borderPadding * self._directionScalar
  self._height = math.abs(currentY - initialY)
end
function IncrementUpdateIterator(currentScalar, node, orientation, directionScalar)
  assert(currentScalar ~= nil and node ~= nil and orientation ~= nil and directionScalar ~= nil, "Nil value was passed into FrameGroup::IncrementUpdateIterator.")
  local heightOrWidth
  if orientation == frameConsts.ORIENTATION.Vertical then
    heightOrWidth = node:GetHeight()
  elseif orientation == frameConsts.ORIENTATION.Horizontal then
    heightOrWidth = node:GetHeight()
  end
  assert(heightOrWidth ~= nil, "Frame length was not defined")
  return currentScalar + heightOrWidth * directionScalar
end
function SetNewPositionScalar(node, value, orientation)
  assert(node ~= nil and value ~= nil and orientation ~= nil, "Nil value was passed into FrameGroup::SetNewPositionScalar.")
  local newPos = node:GetPosition()
  if orientation == frameConsts.ORIENTATION.Vertical then
    newPos.y = value
  else
    newPos.x = value
  end
  node:SetPosition(newPos)
end
function FrameGroup:AddRoot(node)
  assert(node ~= nil, "Nil value passed into FrameGroup:AddRoot")
  self._rootNode = node
end
function FrameGroup:AddNode(frame, index)
  assert(frame ~= nil, "Nil value passed into FrameGroup:AddNode")
  if index == nil then
    index = #self._nodes + 1
  end
  tablex.FastInsert(self._nodes, frame, index)
end
function FrameGroup:SetPadding(amount)
  assert(amount ~= nil, "Nil value passed into FrameGroup:SetPadding")
  self._padding = amount
end
function FrameGroup:SetBorderPadding(amount)
  assert(amount ~= nil, "Nil value passed into FrameGroup:SetBorderPadding")
  self._borderPadding = amount
end
function FrameGroup:SetBackframeScale(backframe)
  local groupHeight = self:GetHeight()
  backframe:SetGOHeight(groupHeight)
end
function FrameGroup:GetRoot()
  return self._rootNode
end
function FrameGroup:GetHeight()
  return self._height
end
function FrameGroup:GetBorderPadding()
  return self._borderPadding
end
function FrameGroup:GetPadding()
  return self._padding
end
function FrameGroup:GetNode(index)
  return self._nodes[index]
end
function FrameGroup:ResetActiveStates()
  for _, frame in pairs(self._nodes) do
    frame:SetActive(true)
  end
end
return {FrameGroup = FrameGroup}
