# Copyright (c) 2013 Mattias Svala
# Copyright (c) 2013 Tao Sauvage
# Copyright (c) 2014 ramnes
# Copyright (c) 2014 Sean Vig
# Copyright (c) 2014 dmpayton
# Copyright (c) 2014 dequis
# Copyright (c) 2014 Tycho Andersen
# Copyright (c) 2015 Serge Hallyn
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import division

from .base import Layout

# This layout implements something akin to wmii's semantics.
#
# Each group starts with one column.  The first window takes up the whole screen.
# Next window splits the column in half.  Windows can be moved to the column to
# the left or right.  If there is no column in the direction being moved into, a
# new column is created.
#
# Each column can be either stacked (each window takes up the whole vertical real
# estate) or split (the windows are split equally vertically in the column)
# Columns can be grown horizontally (cmd_grow_left/right).
#
# My config.py as the following added:

#    Key(
#        [mod, "shift", "control"], "l",
#        lazy.layout.grow_right()
#    ),
#    Key(
#        [mod, "shift"], "l",
#        lazy.layout.shuffle_right()
#    ),
#    Key(
#        [mod, "shift", "control"], "h",
#        lazy.layout.grow_left()
#    ),
#    Key(
#        [mod, "shift"], "h",
#        lazy.layout.shuffle_left()
#    ),
#    Key(
#        [mod], "s",
#        lazy.layout.toggle_split()
#    ),

# We have an array of columns.  Each columns is a dict containing
# width (in percent), rows (an array of rows), # and mode, which is
# either 'stack' or 'split'
#
# Each row is an array of clients

class Wmii(Layout):
    """
        This layout emulates wmii layouts.  The screen it split into
        columns, always starting with one.  A new window is created in
        the active window's column.  Windows can be shifted left and right.
        If there is no column when shifting, a new one is created.
        Each column can be stacked or divided (equally split).
    """
    defaults = [
        ("border_focus", "#881111", "Border colour for the focused window."),
        ("border_normal", "#220000", "Border colour for un-focused windows."),
        ("border_focus_stack", "#0000ff", "Border colour for un-focused windows."),
        ("border_normal_stack", "#000022", "Border colour for un-focused windows."),
        ("grow_amount", 5, "Amount by which to grow/shrink a window."),
        ("border_width", 2, "Border width."),
        ("name", "wmii", "Name of this layout."),
        ("margin", 0, "Margin of the layout"),
    ]

    def __init__(self, **config):
        Layout.__init__(self, **config)
        self.add_defaults(Wmii.defaults)
        self.current_window = None
        self.clients = []
        self.columns = [{'active': 0, 'width': 100, 'mode': 'split', 'rows': []}]

    def info(self):
        d = Layout.info(self)
        d["current_window"] = self.current_window
        d["clients"] = [x.name for x in self.clients]
        return d

    def add_column(self, prepend, win):
        newwidth = int(100 / (len(self.columns) + 1))
        # we are only called if there already is a column, simplifies things
        for c in self.columns:
            c['width'] = newwidth
        c = {'width': newwidth, 'mode': 'split', 'rows': [win]}
        if prepend:
            self.columns.insert(0, c)
        else:
            self.columns.append(c)

    def clone(self, group):
        c = Layout.clone(self, group)
        c.current_window = None
        c.clients = []
        c.columns = [{'active': 0, 'width': 100, 'mode': 'split', 'rows': []}]
        return c

    def current_column(self):
        if self.current_window is None:
            return None
        for c in self.columns:
            if self.current_window in c['rows']:
                return c
        return None

    def add(self, client):
        self.clients.append(client)
        c = self.current_column()
        if c is None:
            if len(self.columns) == 0:
                self.columns = [{'active': 0, 'width': 100, 'mode': 'split', 'rows': []}]
            c = self.columns[0]
        c['rows'].append(client)
        self.focus(client)

    def remove(self, client):
        if client not in self.clients:
            return
        self.clients.remove(client)
        for c in self.columns:
            if client in c['rows']:
                ridx = c['rows'].index(client)
                cidx = self.columns.index(c)
                c['rows'].remove(client)
                if len(c['rows']) != 0:
                    if client == self.current_window:
                        if ridx > 0:
                            ridx -= 1
                        newclient = c['rows'][ridx]
                        self.focus(newclient)
                    self.group.focus(self.current_window)
                    return self.current_window
                # column is now empty, remove it and select the previous one
                self.columns.remove(c)
                if len(self.columns) == 0:
                    return None
                newwidth = int(100 / len(self.columns))
                for c in self.columns:
                    c['width'] = newwidth
                if len(self.columns) == 1:
                    # there is no window at all
                    return None
                if cidx > 0:
                    cidx -= 1
                c = self.columns[cidx]
                rows = c['rows']
                newclient = rows[0]
                self.focus(newclient)
                self.group.focus(newclient)
                return newclient

    def is_last_column(self, cidx):
            return cidx == len(self.columns) - 1

    def focus(self, client):
        self.current_window = client
        for c in self.columns:
            if client in c['rows']:
                c['active'] = c['rows'].index(client)

    def configure(self, client, screen):
        show = True
        if client not in self.clients:
            return
        ridx = -1
        xoffset = int(screen.x)
        for c in self.columns:
            if client in c['rows']:
                ridx = c['rows'].index(client)
                break
            xoffset += int(float(c['width']) * screen.width / 100.0)
        if ridx == -1:
            return
        if client == self.current_window:
            if c['mode'] == 'split':
                px = self.group.qtile.colorPixel(self.border_focus)
            else:
                px = self.group.qtile.colorPixel(self.border_focus_stack)
        else:
            if c['mode'] == 'split':
                px = self.group.qtile.colorPixel(self.border_normal)
            else:
                px = self.group.qtile.colorPixel(self.border_normal_stack)
        if c['mode'] == 'split':
            oneheight = screen.height / len(c['rows'])
            yoffset = int(screen.y + oneheight * ridx)
            win_height = int(oneheight - 2 * self.border_width)
        else:  # stacked
            if c['active'] != c['rows'].index(client):
                show = False
            yoffset = int(screen.y)
            win_height = int(screen.height - 2 * self.border_width)
        win_width = int(float(c['width'] * screen.width / 100.0))
        win_width -= 2 * self.border_width

        if show:
            client.place(
                xoffset,
                yoffset,
                win_width,
                win_height,
                self.border_width,
                px,
                margin=self.margin,
            )
            client.unhide()
        else:
            client.hide()

    def cmd_toggle_split(self):
        c = self.current_column()
        if c['mode'] == "split":
            c['mode'] = "stack"
        else:
            c['mode'] = "split"
        self.group.layoutAll()

    def focus_next(self):
        self.cmd_down()
        return self.curent_window

    def focus_previous(self):
        self.cmd_up()
        return self.current_window

    def focus_first(self):
        if len(self.columns) == 0:
            self.columns = [{'active': 0, 'width': 100, 'mode': 'split', 'rows': []}]
        c = self.columns[0]
        if len(c['rows']) != 0:
            return c['rows'][0]

    def focus_last(self):
        c = self.columns(len(self.columns) - 1)
        if len(c['rows']) != 0:
            return c['rows'][len(c['rows']) - 1]

    def cmd_left(self):
        """
            Switch to the first window on prev column
        """
        c = self.current_column()
        cidx = self.columns.index(c)
        if cidx == 0:
            return
        cidx -= 1
        c = self.columns[cidx]
        if c['mode'] == "split":
            self.group.focus(c['rows'][0])
        else:
            self.group.focus(c['rows'][c['active']])

    def cmd_right(self):
        """
            Switch to the first window on next column
        """
        c = self.current_column()
        cidx = self.columns.index(c)
        if self.is_last_column(cidx):
            return
        cidx += 1
        c = self.columns[cidx]
        if c['mode'] == "split":
            self.group.focus(c['rows'][0])
        else:
            self.group.focus(c['rows'][c['active']])

    def cmd_up(self):
        """
            Switch to the previous window in current column
        """
        c = self.current_column()
        if c is None:
            return
        ridx = c['rows'].index(self.current_window)
        if ridx == 0:
            if c['mode'] != "split":
                ridx = len(c['rows']) - 1
        else:
            ridx -= 1
        client = c['rows'][ridx]
        self.group.focus(client)

    def cmd_down(self):
        """
            Switch to the next window in current column
        """
        c = self.current_column()
        if c is None:
            return
        ridx = c['rows'].index(self.current_window)
        if ridx == len(c['rows']) - 1:
            if c['mode'] != "split":
                ridx = 0
        else:
            ridx += 1
        client = c['rows'][ridx]
        self.group.focus(client)

    cmd_next = cmd_down
    cmd_previous = cmd_up

    def cmd_shuffle_left(self):
        cur = self.current_window
        if cur is None:
            return
        for c in self.columns:
            if cur in c['rows']:
                cidx = self.columns.index(c)
                if cidx == 0:
                    if len(c['rows']) == 1:
                        return
                    c['rows'].remove(cur)
                    self.add_column(True, cur)
                    if len(c['rows']) == 0:
                        self.columns.remove(c)
                else:
                    c['rows'].remove(cur)
                    self.columns[cidx - 1]['rows'].append(cur)
                if len(c['rows']) == 0:
                    self.columns.remove(c)
                    newwidth = int(100 / len(self.columns))
                    for c in self.columns:
                        c['width'] = newwidth
                else:
                    if c['active'] >= len(c['rows']):
                        c['active'] = len(c['rows']) - 1
                self.group.focus(cur)
                return

    def swap_column_width(self, grow, shrink):
        grower = self.columns[grow]
        shrinker = self.columns[shrink]
        amount = self.grow_amount
        if shrinker['width'] - amount < 5:
            return
        grower['width'] += amount
        shrinker['width'] -= amount

    def cmd_grow_left(self):
        cur = self.current_window
        if cur is None:
            return
        for c in self.columns:
            if cur in c['rows']:
                cidx = self.columns.index(c)
                if cidx == 0:
                    # grow left for leftmost-column, shrink left
                    if self.is_last_column(cidx):
                        return
                    self.swap_column_width(cidx + 1, cidx)
                    self.group.focus(cur)
                    return
                self.swap_column_width(cidx, cidx - 1)
                self.group.focus(cur)
                return

    def cmd_grow_right(self):
        cur = self.current_window
        if cur is None:
            return
        for c in self.columns:
            if cur in c['rows']:
                cidx = self.columns.index(c)
                if self.is_last_column(cidx):
                    # grow right from right most, shrink right
                    if cidx == 0:
                        return
                    self.swap_column_width(cidx - 1, cidx)
                    self.group.focus(cur)
                    return
                # grow my width by 20, reduce neighbor to the right by 20
                self.swap_column_width(cidx, cidx + 1)
                self.group.focus(cur)
                return

    def cmd_shuffle_right(self):
        cur = self.current_window
        if cur is None:
            return
        for c in self.columns:
            if cur in c['rows']:
                cidx = self.columns.index(c)
                if self.is_last_column(cidx):
                    if len(c['rows']) == 1:
                        return
                    c['rows'].remove(cur)
                    self.add_column(False, cur)
                    if len(c['rows']) == 0:
                        self.columns.remove(c)
                else:
                    c['rows'].remove(cur)
                    self.columns[cidx + 1]['rows'].append(cur)
                if len(c['rows']) == 0:
                    self.columns.remove(c)
                    newwidth = int(100 / len(self.columns))
                    for c in self.columns:
                        c['width'] = newwidth
                else:
                    if c['active'] >= len(c['rows']):
                        c['active'] = len(c['rows']) - 1
                self.group.focus(cur)
                return

    def cmd_shuffle_down(self):
        for c in self.columns:
            if self.current_window in c['rows']:
                r = c['rows']
                ridx = r.index(self.current_window)
                if ridx + 1 < len(r):
                    r[ridx], r[ridx + 1] = r[ridx + 1], r[ridx]
                    client = r[ridx + 1]
                    self.focus(client)
                    self.group.focus(client)
                return

    def cmd_shuffle_up(self):
        for c in self.columns:
            if self.current_window in c['rows']:
                r = c['rows']
                ridx = r.index(self.current_window)
                if ridx > 0:
                    r[ridx - 1], r[ridx] = r[ridx], r[ridx - 1]
                    client = r[ridx - 1]
                    self.focus(client)
                    self.group.focus(client)
                return
