local super = require "Control"

ButtonBar = super:new()

function ButtonBar:new()
    self = super.new(self)
    
    self.height = 0
    self.useBottomBorder = true
    self.hoveringElement = nil
    self.pressingElement = nil
    self.elements = {}
    self.elementClass = nil
    self.barElement = nil
    
    return self
end

function ButtonBar:unarchiveStyle(style)
    if style == 'button' then
        self.elementClass = ButtonBarElement
        self.barElement = self.elementClass:new()
    elseif style == 'frameless' then
        self.elementClass = ButtonBarFramelessElement
        self.barElement = nil
    end
end

function ButtonBar:draw(canvas)
    local rect = self:rect()
    
    -- measure elements
    local totalWidth = 0
    local fixedWidth = 0
    for index = 1, #self.elements do
        local element = self.elements[index]
        local width = element:getDesiredWidth()
        local minWidth = element:getHeight()
        totalWidth = totalWidth + width + element:getSpacing()
        fixedWidth = fixedWidth + minWidth + element:getSpacing()
    end
    local availableWidth = rect:width()
    if totalWidth > availableWidth then
        for index = 1, #self.elements do
            local adjustment = math.max(0, 1 - (totalWidth - availableWidth) / (totalWidth - fixedWidth))
            local element = self.elements[index]
            local width = element:getDesiredWidth()
            local minWidth = element:getHeight()
            element:setWidth(minWidth + (width - minWidth) * adjustment)
            totalWidth = totalWidth - width - element:getSpacing()
            fixedWidth = fixedWidth - minWidth - element:getSpacing()
            availableWidth = availableWidth - element:getWidth() - element:getSpacing()
        end
    else
        for index = 1, #self.elements do
            local element = self.elements[index]
            element:setWidth(element:getDesiredWidth())
        end
        availableWidth = availableWidth - totalWidth
    end
    local left = 0
    if not self.barElement then
        left = math.floor(availableWidth / 2)
    end
    for index = 1, #self.elements do
        local element = self.elements[index]
        element:setLeft(left)
        left = left + element:getWidth() + element:getSpacing()
    end
    
    -- draw elements
    canvas:preserve(function(canvas)
        if not self.barElement then
            canvas:setPaint(Color.white):fill(Path.rect(rect))
            canvas:concatTransformation(Transformation:identity():translate(math.floor(availableWidth / 2), 0))
        end
        local separatorPath = Path.line{x1 = - 0.5, y1 = rect.bottom, x2 = - 0.5, y2 = rect.top}
        for index = 1, #self.elements do
            local element = self.elements[index]
            element:draw(canvas, self:isActive())
            canvas:concatTransformation(Transformation:identity():translate(element:getWidth() + element:getSpacing(), 0))
            canvas:setThickness(1)
                :setPaint(element:strokePaint())
                :stroke(separatorPath)
        end
        if self.barElement then
            self.barElement:setWidth(rect:width())
            self.barElement:draw(canvas)
        end
    end)
    
    canvas:setThickness(1)
        :setPaint(self:borderPaint())
        :stroke(Path.line{x1 = rect.left, y1 = rect.bottom + 0.5, x2 = rect.right, y2 = rect.bottom + 0.5})
end

function ButtonBar:borderPaint()
    if self.useBottomBorder then
        if self.barElement then
            return Color.gray(0.7)
        else
            return Color.gray(0.6)
        end
    else
        return Color.invisible
    end
end

function ButtonBar:setRect(rect)
    super.setRect(self, rect)
    local height = self:rect():height()
    if self.height ~= height then
        self.height = height
        for index = 1, #self.elements do
            local element = self.elements[index]
            element:setHeight(height)
        end
        if self.barElement then
            self.barElement:setHeight(height)
        end
    end
end

function ButtonBar:addButton(tag, icon, text, font)
    if self.elementClass then
        local element = self.elementClass:new(self.height, tag, icon, text, font)
        self.elements[#self.elements + 1] = element
        element:addObserver(self)
        self:invalidate()
    end
end

function ButtonBar:removeAllButtons()
    self.pressingElement = nil
    for index = 1, #self.elements do
        self.elements[index]:removeObserver(self)
        self.elements[index] = nil
    end
    self:invalidate()
end

function ButtonBar:getButtonIndex(tag)
    for index = 1, #self.elements do
        if self.elements[index]:getTag() == tag then
            return index
        end
    end
end

function ButtonBar:getButtonCount()
    return #self.elements
end

function ButtonBar:getButtonState(index)
    local element = self.elements[index]
    if element then
        return element:getState()
    end
end

function ButtonBar:setButtonState(index, state)
    local element = self.elements[index]
    if element then
        element:setState(state)
        element:setHovering(false)
    end
end

function ButtonBar:getButtonRect(index)
    local element = self.elements[index]
    if element then
        local rect = self:rect()
        local left = element:getLeft()
        return Rect:new{
            left = left,
            bottom = rect.bottom,
            right = left + element:getWidth() + 1,
            top = rect.top,
        }
    end
end

function ButtonBar:getButtonText(index)
    local element = self.elements[index]
    if element then
        return element:getText()
    end
end

function ButtonBar:setBottomBorder(value)
    self.useBottomBorder = value
    self:invalidate()
end

function ButtonBar:move(x, y)
    if self:rect():contains(x, y) then
        for index = 1, #self.elements do
            local element = self.elements[index]
            local left = element:getLeft()
            local right = left + element:getWidth() + 1
            if left <= x and x <= right then
                if self.hoveringElement then
                    self.hoveringElement:setHovering(false)
                end
                self.hoveringElement = element
                self.hoveringElement:setHovering(true)
                return
            end
        end
    end
    if self.hoveringElement then
        self.hoveringElement:setHovering(false)
        self.hoveringElement = nil
    end
end

function ButtonBar:down(x, y)
    if self:rect():contains(x, y) then
        self.pressingElement = nil
        for index = 1, #self.elements do
            local element = self.elements[index]
            local left = element:getLeft()
            local right = left + element:getWidth() + 1
            if left <= x and x <= right then
                self.pressingElement = element
                self.pressingElement:setPressing(true)
                return true
            end
        end
    end
end

function ButtonBar:dragged(x, y)
    local element = self.pressingElement
    if self:rect():contains(x, y) then
        local left = element:getLeft()
        local right = left + element:getWidth() + 1
        element:setPressing(left <= x and x <= right)
    else
        element:setPressing(false)
    end
end

function ButtonBar:up()
    if self.pressingElement:isPressing() then
        self.pressingElement:setPressing(false)
        self:action(self.pressingElement)
        self.pressingElement = nil
    end
end

return ButtonBar
