-- AWMTK isn't exactly a toolkit as much as it is 
-- an addon on top of awesome's native widget capabilities.
-- What it provides primarily is the ability to sub-theme
-- various parts of the ui.
-- Features:
-- - Many new non-standard beautiful variables,
-- - Hierarchic defaults,
-- - Quick widget generators,
-- - Variable prefix generators (like titlebar_bg_normal, etc),
-- - Nightmares,
-- - Why does this exist,
-- - Please don't use it.
local beautiful = require("beautiful")
local wibox = require("wibox")
local awful = require("awful")
local gears = require("gears")
local awmtk = {}

--Global class names 
local classes = {
    "container_",
    "button_",
    "inputbox_",
    "textbox_",
    "icon_"
}
-- Utility functions
local get_child_by_id = function(widget,id,limit)
    local children = {}
    local function _get_child(widget,id)
        for k,v in pairs(widget) do
            if type(v) == "table" then
                if v.id == id then
                    children[#children+1] = v
                end
                _get_child(v,id)
            end
        end
    end
    _get_child({widget},id)
    return children
end
local function key_union(...)
    local tables = {...}
    local keys_lookup = {}
    local keys = {}
    for _,v in pairs(tables) do
        for k,whocareslol in pairs(v) do
            if not keys_lookup[k] then
                keys_lookup[k] = k
                table.insert(keys,k)
            end
        end
    end
    return keys
end
-- Preprocessable boilerplates
local preprocess = {}
preprocess.button = function(style)
    return function(widget,args,callbacks)
        local args = args or {}
        local callbacks = callbacks or {}
        local new_widget = style.button_widget(style)
        local background = get_child_by_id(new_widget,"widget_background")[1]
        for k,v in pairs(args) do
            background[k] = v
        end
        local container = get_child_by_id(new_widget,"widget_container")[1]
        container[#container+1] = widget
        new_widget = wibox.widget(new_widget)
        awmtk.connect_buttons(new_widget,callbacks)
        return new_widget
    end
end
preprocess.icon = function(style)
    return function(widget,args,callbacks)
        local args = args or {}
        local callbacks = callbacks or {}
        local new_widget = style.icon_widget(style)
        local background = get_child_by_id(new_widget,"widget_background")[1]
        for k,v in pairs(args) do
            background[k] = v
        end
        local container = get_child_by_id(new_widget,"widget_container")[1]
        container[#container+1] = widget
        new_widget = wibox.widget(new_widget)
        awmtk.connect_buttons(new_widget,callbacks)
        return new_widget
    end
end
preprocess.inputbox = function(style)
    return function(ps1,args,prompt_args)
        local args = args or {}
        local new_widget = style.inputbox_widget(style,ps1)
        local background = get_child_by_id(new_widget,"widget_background")[1]
        for k,v in pairs(args) do
            background[k] = v
        end
        new_widget = wibox.widget(new_widget)
        local textbox = new_widget:get_children_by_id("widget_prompt")[1]
        awmtk.connect_buttons(new_widget,{function()
            if prompt_args.pre_callback then
                prompt_args.pre_callback(textbox)
            end
            local prompt_options = {
                textbox = textbox,
            }
            for k,v in pairs(prompt_args) do
                if k ~= "textbox" then
                    prompt_options[k] = v
                end
            end
            prompt_options.exe_callback = function(input)
               prompt_args.exe_callback(input,textbox)
               textbox:set_markup_silently(ps1)
            end
            awful.prompt.run(prompt_options)
        end})
        return new_widget
    end
end
preprocess.container = function(style)
    return function(widget,args)
        local args = args or {}
        local new_widget = style.container_widget(style)
        local background = get_child_by_id(new_widget,"widget_background")[1]
        for k,v in pairs(args) do
            background[k] = v
        end
        local container = get_child_by_id(new_widget,"widget_container")[1]
        container[#container+1] = widget
        new_widget = wibox.widget(new_widget)
        return new_widget
    end
end
preprocess.shape = function(style)
    return function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, style.rounding)
    end
end
preprocess.container_shape = function(style)
    return function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, style.container_rounding or style.rounding)
    end
end
preprocess.button_shape = function(style)
    return function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, style.button_rounding or style.rounding)
    end
end
preprocess.icon_shape = function(style)
    return function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, style.icon_rounding or style.rounding)
    end
end
preprocess.inputbox_shape = function(style)
    return function(cr, width, height)
        gears.shape.rounded_rect(cr, width, height, style.inputbox_rounding or style.rounding)
    end
end
-- AWMTK functions

awmtk.connect_buttons = function(new_widget,callbacks)
    new_widget:buttons (
        awful.util.table.join(
            (callbacks[1] and awful.button({},1,callbacks[1],callbacks.release_1)),
            (callbacks[2] and awful.button({},2,callbacks[2],callbacks.release_2)),
            (callbacks[3] and awful.button({},3,callbacks[3],callbacks.release_3))
        )
    )
    return new_widget
end

awmtk._create_preprocess_style = function(style,prefix)
    -- We use this function to force templates to favor prefix values
    new_style = setmetatable({}, {
        __index = function(t,k)
            if type(k) == "string" and style[prefix..k] then
                return style[prefix..k]
            end
            return style[k]
        end
    })
    return new_style
end
awmtk.create_prefix = function(style,prefix,index)
    -- Create a prefix for new widgets to use
    local keys = index
    for _,k in pairs(keys) do
        style[prefix..k] = style[prefix..k] or style[k]
    end
    return style
end
awmtk.style = function(parent,delta,prefix)
    local new_style = setmetatable({},{
        __index = function(t,k)
            return rawget(t,k) or
            delta[k] or
            parent[k]
        end
    })
    if prefix then
        new_style = awmtk.create_prefix(new_style,prefix,key_union(parent,delta))
    end
    local _new_style = awmtk._create_preprocess_style(new_style,prefix)
    _new_style.button_shape = preprocess.button_shape(_new_style)
    _new_style.icon_shape = preprocess.icon_shape(_new_style)
    _new_style.container_shape = preprocess.container_shape(_new_style)
    _new_style.inputbox_shape = preprocess.inputbox_shape(_new_style)
    new_style.button = preprocess.button(_new_style)
    new_style.icon = preprocess.icon(_new_style)
    new_style.container = preprocess.container(_new_style)
    new_style.inputbox = preprocess.inputbox(_new_style)
    return new_style
end
-- Default variables table
defaults = setmetatable({},{
    __index = beautiful
})
defaults.bg_normal = defaults.bg_normal
defaults.bg_focus = defaults.bg_focus
defaults.bg_urgent = defaults.bg_urgent
defaults.bg_marked = defaults.bg_marked
defaults.fg_normal = defaults.fg_normal
defaults.fg_focus = defaults.fg_focus
defaults.fg_urgent = defaults.fg_urgent
defaults.fg_marked = defaults.fg_marked
defaults.border_width = defaults.border_width
defaults.border_normal = defaults.border_normal
defaults.border_focus = defaults.border_focus
defaults.border_marked = defaults.border_marked
defaults.shape_border_width = defaults.shape_border_width or 0
defaults.shape_border_color = defaults.shape_border_color or defaults.bg_normal
-- Extra variables
defaults.inner_margin = defaults.inner_margin or 5
defaults.rounding = defaults.rounding or 0
-- Generate classes variables
local new_defaults = {}
for _,class in pairs(classes) do
    for parameter,value in pairs(defaults) do
        new_defaults[class..parameter] = (      
            defaults[class..parameter] or
            value 
        )
    end
end
for parameter,value in pairs(defaults) do
    new_defaults[parameter] = value
end
defaults = setmetatable(new_defaults,{
    __index = beautiful
})
--make icons look less weird
defaults.icon_inner_margin = beautiful.icon_inner_margin or 0
--add spacing for lists
defaults.container_spacing = defaults.container_spacing or 2
defaults.container_spacing_vertical = defaults.container_spacing_vertical or defaults.container_spacing
defaults.container_spacing_horizontal = defaults.container_spacing_horizontal or defaults.container_spacing
--add button size defaults
defaults.button_size = 20
defaults.button_height = defaults.button_size
defaults.button_width = defaults.button_size
--add icon size defaults
defaults.icon_size = 20
defaults.icon_height = defaults.icon_size
defaults.icon_width = defaults.icon_size
-- Templates for quick widget generation
defaults.button_widget = defaults.button_widget or function(style) 
    return {
        {
            id = "widget_container",
            widget = wibox.container.margin,
            margins = style.button_inner_margin,
        },
        bg = style.button_bg_normal,
        id = "widget_background",
        widget = wibox.container.background,
        shape = style.button_shape,
        shape_border_width = style.button_shape_border_width,
        shape_border_color = style.button_shape_border_color,
        fg = style.button_fg_normal
    }
end
defaults.icon_widget = defaults.icon_widget or function(style)
    return {
        {
            id = "widget_container",
            widget = wibox.container.margin,
            margins = style.icon_inner_margin,
        },
        bg = style.icon_bg_normal,
        id = "widget_background",
        widget = wibox.container.background,
        shape = style.icon_shape,
        shape_border_width = style.icon_shape_border_width,
        shape_border_color = style.icon_shape_border_color,
        fg = style.icon_fg_normal
    }
end
defaults.container_widget = defaults.container_widget or function(style)
    return {
        {
            id = "widget_container",
            widget = wibox.container.margin,
            margins = style.container_inner_margin,
        },
        bg = style.container_bg_normal,
        id = "widget_background",
        widget = wibox.container.background,
        shape = style.container_shape,
        shape_border_width = style.container_shape_border_width,
        shape_border_color = style.container_shape_border_color,
        fg = style.container_fg_normal
    }
end
defaults.inputbox_widget = defaults.inputbox_widget or function(style,prompt)
    return {
        {
            {
                id = "widget_prompt",
                markup = prompt or "",
                widget = wibox.widget.textbox,
                fg = style.inputbox_fg_normal
            },
            id = "widget_container",
            widget = wibox.container.margin,
            margins = style.inputbox_inner_margin,
        },
        bg = style.inputbox_bg_normal,
        id = "widget_background",
        widget = wibox.container.background,
        shape = style.inputbox_shape,
        shape_border_width = style.inputbox_shape_border_width,
        shape_border_color = style.inputbox_shape_border_color
    }
end
defaults.button_shape = defaults.button_shape or preprocess.button_shape(defaults)
defaults.icon_shape = defaults.icon_shape or preprocess.icon_shape(defaults)
defaults.container_shape = defaults.container_shape or preprocess.container_shape(defaults)
defaults.button = preprocess.button(defaults)
defaults.icon = preprocess.icon(defaults)
defaults.container = preprocess.container(defaults)
defaults.inputbox = preprocess.inputbox(defaults)
awmtk.defaults = defaults
awmtk.preprocess = preprocess
awmtk.utils = {
    key_union = key_union,
    get_child_by_id = get_child_by_id
}
return awmtk
