"""
This plugin exports four functions - up, down, left and right - that when called will
move window focus to the first window in that general direction. Focussing is based
entirely on position and geometry, so is independent of screens, layouts and whether
windows are floating or tiled. It can also move focus to and from empty screens.

Example usage:

    import traverse

    keys.extend([
        Key([mod], 'k', lazy.function(traverse.up)),
        Key([mod], 'j', lazy.function(traverse.down)),
        Key([mod], 'h', lazy.function(traverse.left)),
        Key([mod], 'l', lazy.function(traverse.right)),
    ])

Qtile versions known to work: 0.16 - 0.18
"""

from libqtile.config import Screen


def up(qtile):
    _focus_window(qtile, -1, 'y')


def down(qtile):
    _focus_window(qtile, 1, 'y')


def left(qtile):
    _focus_window(qtile, -1, 'x')


def right(qtile):
    _focus_window(qtile, 1, 'x')



def get_window_in_direction(qtile, dir, axis):
    win = None
    win_wide = None
    dist = 10000
    dist_wide = 10000
    cur = qtile.current_window
    if not cur:
        cur = qtile.current_screen

    if axis == 'x':
        dim = 'width'
        band_axis = 'y'
        band_dim = 'height'
        cur_pos = cur.x
        band_min = cur.y
        band_max = cur.y + cur.height
    else:
        dim = 'height'
        band_axis = 'x'
        band_dim = 'width'
        band_min = cur.x
        cur_pos = cur.y
        band_max = cur.x + cur.width

    cur_pos += getattr(cur, dim) / 2

    windows = [w for g in qtile.groups if g.screen for w in g.windows]
    windows.extend([s for s in qtile.screens if not s.group.windows])

    if cur in windows:
        windows.remove(cur)

    for w in windows:
        if isinstance(w, Screen) or not w.minimized:
            pos = getattr(w, axis) + getattr(w, dim) / 2
            gap = dir * (pos - cur_pos)
            if gap > 5:
                band_pos = getattr(w, band_axis) + getattr(w, band_dim) / 2
                if band_min < band_pos < band_max:
                    if gap < dist:
                        dist = gap
                        win = w
                else:
                    if gap < dist_wide:
                        dist_wide = gap
                        win_wide = w

    if not win:
        win = win_wide

    return win


def _focus_window(qtile, dir, axis):
    win = get_window_in_direction(qtile, dir, axis)
    if win:
        qtile.focus_screen(win.group.screen.index)
        win.group.focus(win, True)
        if not isinstance(win, Screen):
            win.focus(False)
