from efl.evas import EVAS_HINT_EXPAND, EVAS_HINT_FILL, Rectangle
from efl.ecore import Timer
from efl.elementary.box import Box
from efl.elementary.spinner import Spinner
from efl.elementary.hoversel import Hoversel
from efl.elementary.label import Label
from efl.elementary.notify import Notify
from efl.elementary.popup import Popup
from efl.elementary.button import Button
from efl.elementary.grid import Grid
from efl.elementary.fileselector import Fileselector
from efl.elementary.fileselector_button 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


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_complete_pieces=0, *args, **kwargs):

        self.num_complete_pieces = num_complete_pieces

        self._blocks = None
        self._block_size = None
        self.rects = []

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

        self.pieces = pieces
        self.num_total_pieces = len(pieces)

        self.pack_rects()

    def block_size_get(self):
        if self._block_size is not None:
            return self._block_size

        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)

        self._block_size = block_size
        return block_size

    block_size = property(block_size_get)

    def blocks_get(self):
        if self._blocks is not None:
            return self._blocks

        blocks = []

        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)

        self._blocks = blocks
        return blocks

    blocks = property(blocks_get)

    def pack_rects(self):
        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):
        width, height = self.size
        old_blocks = self.blocks
        self.pieces = pieces
        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)
