-- @docclass
UIScrollArea = extends(UIWidget, "UIScrollArea")

-- public functions
function UIScrollArea.create()
  local scrollarea = UIScrollArea.internalCreate()
  scrollarea:setClipping(true)
  scrollarea.inverted = false
  scrollarea.alwaysScrollMaximum = false
  return scrollarea
end

function UIScrollArea:onStyleApply(styleName, styleNode)
  for name,value in pairs(styleNode) do
    if name == 'vertical-scrollbar' then
      addEvent(function()
        local parent = self:getParent()
        if parent then
          self:setVerticalScrollBar(parent:getChildById(value))
        end
      end)
    elseif name == 'horizontal-scrollbar' then
      addEvent(function()
        local parent = self:getParent()
        if parent then
          self:setHorizontalScrollBar(self:getParent():getChildById(value))
        end
      end)
    elseif name == 'inverted-scroll' then
      self:setInverted(value)
    elseif name == 'always-scroll-maximum' then
      self:setAlwaysScrollMaximum(value)
    end
  end
end

function UIScrollArea:updateScrollBars()
  local scrollWidth = math.max(self:getChildrenRect().width - self:getPaddingRect().width, 0)
  local scrollHeight = math.max(self:getChildrenRect().height - self:getPaddingRect().height, 0)

  local scrollbar = self.verticalScrollBar
  if scrollbar then
    if self.inverted then
      scrollbar:setMinimum(-scrollHeight)
      scrollbar:setMaximum(0)
    else
      scrollbar:setMinimum(0)
      scrollbar:setMaximum(scrollHeight)
    end
  end

  local scrollbar = self.horizontalScrollBar
  if scrollbar then
    if self.inverted then
      scrollbar:setMinimum(-scrollWidth)
      scrollbar:setMaximum(0)
    else
      scrollbar:setMinimum(0)
      scrollbar:setMaximum(scrollWidth)
    end
  end

  if self.lastScrollWidth ~= scrollWidth then
    self:onScrollWidthChange()
  end
  if self.lastScrollHeight ~= scrollHeight then
    self:onScrollHeightChange()
  end

  self.lastScrollWidth = scrollWidth
  self.lastScrollHeight = scrollHeight
end

function UIScrollArea:setVerticalScrollBar(scrollbar)
  self.verticalScrollBar = scrollbar
  connect(self.verticalScrollBar, 'onValueChange', function(scrollbar, value)
    local virtualOffset = self:getVirtualOffset()
    virtualOffset.y = value
    self:setVirtualOffset(virtualOffset)
    signalcall(self.onScrollChange, self, virtualOffset)
  end)
  self:updateScrollBars()
end

function UIScrollArea:setHorizontalScrollBar(scrollbar)
  self.horizontalScrollBar = scrollbar
  connect(self.horizontalScrollBar, 'onValueChange', function(scrollbar, value)
    local virtualOffset = self:getVirtualOffset()
    virtualOffset.x = value
    self:setVirtualOffset(virtualOffset)
    signalcall(self.onScrollChange, self, virtualOffset)
  end)
  self:updateScrollBars()
end

function UIScrollArea:setInverted(inverted)
  self.inverted = inverted
end

function UIScrollArea:setAlwaysScrollMaximum(value)
  self.alwaysScrollMaximum = value
end

function UIScrollArea:onLayoutUpdate()
  self:updateScrollBars()
end

function UIScrollArea:onMouseWheel(mousePos, mouseWheel)
  if self.verticalScrollBar then
    if not self.verticalScrollBar:isOn() then
      return false
    end
    if mouseWheel == MouseWheelUp then
      local minimum = self.verticalScrollBar:getMinimum()
      if self.verticalScrollBar:getValue() <= minimum then
        return false
      end
      self.verticalScrollBar:decrement()
    else
      local maximum = self.verticalScrollBar:getMaximum()
      if self.verticalScrollBar:getValue() >= maximum then
        return false
      end
      self.verticalScrollBar:increment()
    end
  elseif self.horizontalScrollBar then
    if not self.horizontalScrollBar:isOn() then
      return false
    end
    if mouseWheel == MouseWheelUp then
      local maximum = self.horizontalScrollBar:getMaximum()
      if self.horizontalScrollBar:getValue() >= maximum then
        return false
      end
      self.horizontalScrollBar:increment()
    else
      local minimum = self.horizontalScrollBar:getMinimum()
      if self.horizontalScrollBar:getValue() <= minimum then
        return false
      end
      self.horizontalScrollBar:decrement()
    end
  end
  return true
end

function UIScrollArea:ensureChildVisible(child)
  if child then
    local paddingRect = self:getPaddingRect()
    if self.verticalScrollBar then
      local deltaY = paddingRect.y - child:getY()
      if deltaY > 0 then
        self.verticalScrollBar:decrement(deltaY)
      end

      deltaY = (child:getY() + child:getHeight()) - (paddingRect.y + paddingRect.height)
      if deltaY > 0 then
        self.verticalScrollBar:increment(deltaY)
      end
    elseif self.horizontalScrollBar then
      local deltaX = paddingRect.x - child:getX()
      if deltaX > 0 then
        self.horizontalScrollBar:decrement(deltaX)
      end

      deltaX = (child:getX() + child:getWidth()) - (paddingRect.x + paddingRect.width)
      if deltaX > 0 then
        self.horizontalScrollBar:increment(deltaX)
      end
    end
  end
end

function UIScrollArea:onChildFocusChange(focusedChild, oldFocused, reason)
  if focusedChild and (reason == MouseFocusReason or reason == KeyboardFocusReason) then
    self:ensureChildVisible(focusedChild)
  end
end

function UIScrollArea:onScrollWidthChange()
  if self.alwaysScrollMaximum and self.horizontalScrollBar then
    self.horizontalScrollBar:setValue(self.horizontalScrollBar:getMaximum())
  end
end

function UIScrollArea:onScrollHeightChange()
  if self.alwaysScrollMaximum and self.verticalScrollBar then
    self.verticalScrollBar:setValue(self.verticalScrollBar:getMaximum())
  end
end
