--[[

     Licensed under GNU General Public License v2
      * (c) 2014,      projektile, worron
      * (c) 2013,      Luke Bonham
      * (c) 2012,      Josh Komoroske
      * (c) 2010-2012, Peter Hofmann

--]]

local beautiful = require("beautiful")
local ipairs    = ipairs
local math      = { ceil = math.ceil, sqrt = math.sqrt, floor = math.floor, max = math.max }
local tonumber  = tonumber

local uselessfair = {}

-- Transformation functions
local function swap(geometry)
    return { x = geometry.y, y = geometry.x, width = geometry.height, height = geometry.width }
end

-- Client geometry correction depending on useless gap and window border
local function size_correction(c, geometry, useless_gap)
    geometry.width  = math.max(geometry.width  - 2 * c.border_width - useless_gap, 1)
    geometry.height = math.max(geometry.height - 2 * c.border_width - useless_gap, 1)
    geometry.x = geometry.x + useless_gap / 2
    geometry.y = geometry.y + useless_gap / 2
end

-- Main tiling function
local function fair(p, orientation)

    -- Theme vars
    local useless_gap = beautiful.useless_gap_width or 0
    local global_border = beautiful.global_border_width or 0

    -- Aliases
    local wa = p.workarea
    local cls = p.clients

    -- Nothing to tile here
    if #cls == 0 then return end

    -- Workarea size correction depending on useless gap and global border
    wa.height = wa.height - 2 * global_border - useless_gap
    wa.width  = wa.width -  2 * global_border - useless_gap
    wa.x = wa.x + useless_gap / 2 + global_border
    wa.y = wa.y + useless_gap / 2 + global_border

    -- Geometry calculation
    local row, col = 0, 0

    local rows = math.ceil(math.sqrt(#cls))
    local cols = math.ceil(#cls / rows)

    for i, c in ipairs(cls) do
        local g = {}

        -- find tile orientation for current client and swap geometry if need
        local need_swap = (orientation == "east" and #cls <= 2) or (orientation == "south" and #cls > 2)
        local area = need_swap and swap(wa) or wa

        -- calculate geometry
        if #cls < (cols * rows) and row == cols - 1 then
            g.width = area.width / (rows - ((cols * rows) - #cls))
        else
            g.width = area.width / rows
        end

        g.height = area.height / cols
        g.x = area.x + col * g.width
        g.y = area.y + row * g.height

        -- turn back to real if geometry was swapped
        if need_swap then g = swap(g) end

        -- window size correction depending on useless gap and window border
        size_correction(c, g, useless_gap)

        -- set geometry
        c:geometry(g)

        -- update tile grid coordinates
        col = i % rows
        row = math.floor(i / rows)
    end
end

-- Layout constructor
local function construct_layout(name, direction)
    return {
        name = name,
        -- @p screen The screen number to tile
        arrange = function(p) return fair(p, direction) end
    }
end

-- Build layouts with different tile direction
uselessfair.vertical   = construct_layout("uselessfair", "south")
uselessfair.horizontal = construct_layout("uselessfairh", "east")

-- Module aliase
uselessfair.arrange = uselessfair.vertical.arrange
uselessfair.name = uselessfair.vertical.name

return uselessfair