-- Tab Bar Graphics Element

local util    = require("scada-common.util")

local core    = require("graphics.core")
local element = require("graphics.element")

---@class tabbar_tab
---@field name string tab name
---@field color cpair tab colors (fg/bg)
---@field _start_x integer starting touch x range (inclusive)
---@field _end_x integer ending touch x range (inclusive)

---@class tabbar_args
---@field tabs table tab options
---@field callback function function to call on tab change
---@field min_width? integer text length + 2 if omitted
---@field parent graphics_element
---@field id? string element id
---@field x? integer 1 if omitted
---@field y? integer auto incremented if omitted
---@field width? integer parent width if omitted
---@field fg_bg? cpair foreground/background colors
---@field hidden? boolean true to hide on initial draw

-- Create a new tab selector control element.
---@param args tabbar_args
---@return TabBar element, element_id id
return function (args)
    element.assert(type(args.tabs) == "table", "tabs is a required field")
    element.assert(#args.tabs > 0, "at least one tab is required")
    element.assert(type(args.callback) == "function", "callback is a required field")
    element.assert(type(args.min_width) == "nil" or (type(args.min_width) == "number" and args.min_width > 0), "min_width must be nil or a number > 0")

    args.height = 1

    -- determine widths
    local max_width = 1
    for i = 1, #args.tabs do
        local opt = args.tabs[i]    ---@type tabbar_tab
        if string.len(opt.name) > max_width then
            max_width = string.len(opt.name)
        end
    end

    local button_width = math.max(max_width, args.min_width or 0)

    -- create new graphics element base object
    local e = element.new(args --[[@as graphics_args]])

    element.assert(e.frame.w >= (button_width * #args.tabs), "width insufficent to display all tabs")

    -- default to 1st tab
    e.value = 1

    -- calculate required tab dimension information
    local next_x = 1
    for i = 1, #args.tabs do
        local tab = args.tabs[i] ---@type tabbar_tab

        tab._start_x = next_x
        tab._end_x = next_x + button_width - 1

        next_x = next_x + button_width
    end

    -- show the tab state
    function e.redraw()
        for i = 1, #args.tabs do
            local tab = args.tabs[i]    ---@type tabbar_tab

            e.w_set_cur(tab._start_x, 1)

            if e.value == i then
                e.w_set_fgd(tab.color.fgd)
                e.w_set_bkg(tab.color.bkg)
            else
                e.w_set_fgd(e.fg_bg.fgd)
                e.w_set_bkg(e.fg_bg.bkg)
            end

            e.w_write(util.pad(tab.name, button_width))
        end
    end

    -- check which tab a given x is within
    ---@return integer|nil button index or nil if not within a tab
    local function which_tab(x)
        for i = 1, #args.tabs do
            local tab = args.tabs[i]    ---@type tabbar_tab
            if x >= tab._start_x and x <= tab._end_x then return i end
        end

        return nil
    end

    -- handle mouse interaction
    ---@param event mouse_interaction mouse event
    function e.handle_mouse(event)
        -- determine what was pressed
        if e.enabled and core.events.was_clicked(event.type) and e.in_frame_bounds(event.current.x, event.current.y) then
            -- a button may have been pressed, which one was it?
            local tab_ini = which_tab(event.initial.x)
            local tab_cur = which_tab(event.current.x)

            -- mouse up must always have started with a mouse down on the same tab to count as a click
            -- tap always has identical coordinates, so this always passes for taps
            if tab_ini == tab_cur and tab_cur ~= nil then
                e.value = tab_cur
                e.redraw()
                args.callback(e.value)
            end
        end
    end

    -- set the value
    ---@param val integer new value
    function e.set_value(val)
        e.value = val
        e.redraw()
    end

    ---@class TabBar:graphics_element
    local TabBar, id = e.complete(true)

    return TabBar, id
end
