#
#  Epour - A bittorrent client using EFL and libtorrent
#
#  Copyright 2012-2017 Kai Huuhko <kai.huuhko@gmail.com>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#  MA 02110-1301, USA.
#

from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, Rectangle
from efl.ecore import Timer
from efl.elementary import Box
from efl.elementary import Spinner
from efl.elementary import Hoversel
from efl.elementary import Label
from efl.elementary import Notify
from efl.elementary import Popup
from efl.elementary import Button
from efl.elementary import Grid
from efl.elementary import Fileselector
from efl.elementary import FileselectorButton

EXPAND_BOTH = EVAS_HINT_EXPAND, EVAS_HINT_EXPAND
EXPAND_HORIZ = EVAS_HINT_EXPAND, 0.0
FILL_BOTH = EVAS_HINT_FILL, EVAS_HINT_FILL
FILL_HORIZ = EVAS_HINT_FILL, 0.5

if "xrange" not in dir(__builtins__):
    xrange = range


def chunker(seq, size):
    return (seq[pos:pos + size] for pos in xrange(0, len(seq), size))


class UnitSpinner(Box):

    si_prefixes = ("", "k", "M", "G", "T", "P")
    binary_prefixes = ("", "Ki", "Mi", "Gi", "Ti", "Pi")

    def __init__(self, parent, unit, base=1000, prefixes=si_prefixes):

        self.unit = unit
        self.base = base
        self.prefixes = prefixes

        super(UnitSpinner, self).__init__(parent)
        self.horizontal = True

        self.save_timer = None

        s = self.spinner = Spinner(parent)
        s.size_hint_weight = EXPAND_HORIZ
        s.size_hint_align = FILL_HORIZ
        s.min_max = 0, base
        s.show()
        self.pack_end(s)

        hs = self.hoversel = Hoversel(parent)
        hs.hover_parent = parent.top_widget
        for p in prefixes:
            hs.item_add(p + unit)
        hs.show()
        self.pack_end(hs)

        hs.callback_selected_add(lambda x, y: x.text_set(y.text))

    def callback_changed_add(self, func, delay=None):
        self.spinner.callback_changed_add(self.changed_cb, func, delay)
        self.hoversel.callback_selected_add(self.changed_cb, func, delay)

    def changed_cb(self, widget, *args):
        func, delay = args[-2:]

        if delay:
            if self.save_timer is not None:
                self.save_timer.delete()

            self.save_timer = Timer(2.0, self.save_cb, func)
        else:
            self.save_cb(func)

    def save_cb(self, func):
        v = int(self.get_value())
        func(self, v)
        self.save_timer = None
        return False

    def get_value(self):
        p = self.hoversel.text[:-len(self.unit)]
        return self.spinner.value * (
            self.base ** self.prefixes.index(p)
            )

    def set_value(self, v):
        i = 0

        while v // self.base > 0:
            i += 1
            v = float(v) / float(self.base)

        if i > len(self.prefixes):
            i = -1

        self.spinner.value = v
        self.hoversel.text = self.prefixes[i] + self.unit


class Information(Notify):
    def __init__(self, parent, text, button=False, timeout=3, *args, **kwargs):
        super(self.__class__, self).__init__(parent)
        b = Box(self)
        l = Label(b, text=text)
        b.pack_end(l)
        l.show()
        self.content = l
        if button:
            xbtn = Button(b, text=_("Close"))
            xbtn.callback_clicked_add(lambda x: self.delete())
            b.pack_end(xbtn)
        self.timeout = timeout
        self.show()


class Error(Popup):
    def __init__(self, parent, title, text):
        super(self.__class__, self).__init__(parent)
        self.part_text_set("title,text", title)
        self.text = text
        b = Button(self)
        b.text = _("OK")
        b.callback_clicked_add(lambda x: self.delete())
        self.part_content_set("button1", b)
        self.show()


class ConfirmExit(Popup):
    def __init__(self, parent, exit_func):
        super(self.__class__, self).__init__(parent)
        self.part_text_set("title,text", _("Confirm exit"))
        self.text = _("Are you sure you wish to exit Epour?")
        b = Button(self)
        b.text = _("Yes")
        b.callback_clicked_add(lambda x: exit_func())
        self.part_content_set("button1", b)
        b = Button(self)
        b.text = _("No")
        b.callback_clicked_add(lambda x: self.delete())
        self.part_content_set("button2", b)
        self.show()


class BlockGraph(Grid):

    colors = (
        (255, 0, 0, 255),
        (255, 255, 0, 255),
        (0, 255, 0, 255)
    )

    def __init__(self, parent, pieces, num_completed_pieces=0, *args, **kwargs):

        self.num_completed_pieces = num_completed_pieces
        self.pieces = pieces

        self.rects = []

        super(self.__class__, self).__init__(parent, *args, **kwargs)

        self.pack_rects()

    @property
    def num_total_pieces(self):
        return len(self.pieces)

    @property
    def block_size(self):
        width, height = self.size
        num_pieces = self.num_total_pieces
        grid_size = width * height
        block_size = \
            num_pieces//grid_size + bool(num_pieces % grid_size)

        return block_size

    @property
    def blocks(self):
        blocks = []

        if self.block_size == 0:
            raise ValueError("Block size 0")

        for block in chunker(self.pieces, self.block_size):
            if all(block):
                blocks.append(2)
            elif any(block):
                blocks.append(1)
            else:
                blocks.append(0)

        return blocks

    def pack_rects(self):
        if self.num_total_pieces == 0:
            return
        blocks = self.blocks
        width, height = self.size
        len_blocks = len(blocks)

        p = 0
        for y in xrange(height):
            for x in xrange(width):
                if p >= len_blocks:
                    continue
                else:
                    block = blocks[p]
                    if block == 0:
                        color = self.colors[0]
                    elif block == 1:
                        color = self.colors[1]
                    else:
                        color = self.colors[2]

                rect = Rectangle(self.evas, color=color)
                self.rects.append(rect)
                self.pack(rect, x, y, 1, 1)
                rect.show()
                p += 1

    def update(self, pieces, num_pieces):
        self.pieces = pieces
        if pieces and not self.rects:
            self.pack_rects()
        elif self.num_completed_pieces == num_pieces:
            return
        self.num_completed_pieces = num_pieces
        width, height = self.size
        old_blocks = self.blocks
        self._blocks = None
        new_blocks = self.blocks
        row = 0
        col = 0
        for ov, nv in zip(old_blocks, new_blocks):
            if ov != nv:
                self.rects[(row * width) + col].color = self.colors[nv]

            if (col + 1) >= width:
                row += 1
                col = 0
            else:
                col += 1

        return True


class ActSWithLabel(Box):

    def __init__(self, parent, label_text, values, initial_value):
        Box.__init__(self, parent)

        from efl.elementary.actionslider import Actionslider, \
            ELM_ACTIONSLIDER_LEFT, ELM_ACTIONSLIDER_CENTER, \
            ELM_ACTIONSLIDER_RIGHT, ELM_ACTIONSLIDER_ALL

        self.pos_to_v = {
            ELM_ACTIONSLIDER_LEFT: values[0],
            ELM_ACTIONSLIDER_CENTER: values[1],
            ELM_ACTIONSLIDER_RIGHT: values[2],
        }
        self.v_to_pos = {
            values[0]: ELM_ACTIONSLIDER_LEFT,
            values[1]: ELM_ACTIONSLIDER_CENTER,
            values[2]: ELM_ACTIONSLIDER_RIGHT,
        }

        self.horizontal = True
        self.size_hint_align = FILL_HORIZ
        self.size_hint_weight = EXPAND_HORIZ

        l = Label(parent)
        l.text = label_text
        l.show()
        w = self.w = Actionslider(parent)
        w.magnet_pos = ELM_ACTIONSLIDER_ALL
        w.size_hint_align = FILL_HORIZ
        w.size_hint_weight = EXPAND_HORIZ
        w.show()

        parts = "left", "center", "right"

        for i, v in enumerate(values):
            w.part_text_set(parts[i], str(v))

        init_v = self.v_to_pos[initial_value]
        w.indicator_pos = init_v

        self.pack_end(l)
        self.pack_end(w)

    def get_value(self):
        pos = self.w.indicator_pos
        value = self.pos_to_v[pos]
        return value


class RangeSpinners(Box):

    def __init__(self, parent, low, high, minim, maxim):

        Box.__init__(self, parent)
        self.size_hint_weight = EXPAND_BOTH
        self.size_hint_align = FILL_BOTH
        self.horizontal = True

        l = self.listenlow = Spinner(parent)
        l.size_hint_weight = EXPAND_BOTH
        l.size_hint_align = FILL_BOTH
        l.min_max = minim, maxim
        l.value = low
        self.pack_end(l)
        l.show()

        h = self.listenhigh = Spinner(parent)
        h.size_hint_weight = EXPAND_BOTH
        h.size_hint_align = FILL_BOTH
        h.min_max = minim, maxim
        h.value = high
        self.pack_end(h)
        h.show()


class FsButton(Fileselector, FileselectorButton):

    def __init__(self, parent, *args, **kwargs):
        FileselectorButton.__init__(self, parent, *args, **kwargs)
