# ---------------------------------------------------------------------------
#
#    Albow - Pull-down or pop-up menu
#
#---------------------------------------------------------------------------
#-# Modified by D.C.-G. for translation purpose
import sys
from root import get_root, get_focus
from dialogs import Dialog
from theme import ThemeProperty
from pygame import Rect, draw
from translate import _
#---------------------------------------------------------------------------


class MenuItem(object):
    keyname = ""
    keycode = None
    shift = False
    alt = False
    enabled = False

    if sys.platform.startswith('darwin') or sys.platform.startswith('mac'):
        cmd_name = "Cmd "
        option_name = "Opt "
    else:
        cmd_name = "Ctrl "
        option_name = "Alt "

    def __init__(self, text="", command=None, doNotTranslate=False):
        self.command = command
        if "/" in text:
            text, key = text.split("/", 1)
        else:
            key = ""
        self.text = _(text, doNotTranslate=doNotTranslate)
        if key:
            keyname = key[-1]
            mods = key[:-1]
            self.keycode = ord(keyname.lower())
            if "^" in mods:
                self.shift = True
                keyname = "Shift " + keyname
            if "@" in mods:
                self.alt = True
                keyname = self.option_name + keyname
            self.keyname = self.cmd_name + keyname


#---------------------------------------------------------------------------


class Menu(Dialog):
    disabled_color = ThemeProperty('disabled_color')
    click_outside_response = -1
    scroll_button_size = ThemeProperty('scroll_button_size')
    scroll_button_color = ThemeProperty('scroll_button_color')
    scroll = 0

    def __init__(self, title, items, scrolling=False, scroll_items=30,
                 scroll_page=5, handler=None, **kwds):
        self.handler = handler
        self.title = _(title, doNotTranslate=kwds.get('doNotTranslate', False))
        self.items = items
        self._items = [MenuItem(*item, doNotTranslate=kwds.get('doNotTranslate', False)) for item in items]
        self.scrolling = scrolling and len(self._items) > scroll_items
        self.scroll_items = scroll_items
        self.scroll_page = scroll_page

        self._selected_item_index = 0
        self._hilited = self._items[self._selected_item_index]

        Dialog.__init__(self, **kwds)

        self.root = self.get_root()
        self.calc_size()

    #&#
    def set_items(self, items):
        self.items = items
        self._items = [MenuItem(*item, doNotTranslate=self.doNotTranslate) for item in items]

    def set_scroll_items(self, scroll_items):
        if scroll_items < len(self.items):
            self.scroll_items = scroll_items
        else:
            self.scroll_items = len(self.items)
        self.calc_size()
    #&#

    def calc_size(self):
        h = self.font.get_linesize()
        if self.scrolling:
            self.height = h * self.scroll_items + h
        else:
            self.height = h * len(self._items) + h

    def set_update_ui(self, v):
        if v:
            self._items = [MenuItem(*item, doNotTranslate=self.doNotTranslate) for item in self.items]
        Dialog.set_update_ui(self, v)

    def present(self, client, pos):
        client = client or self.root
        self.topleft = client.local_to_global(pos)
        focus = self.handler or get_focus()
        font = self.font
        h = font.get_linesize()
        items = self._items
        margin = self.margin
        if self.scrolling:
            height = h * self.scroll_items + h
        else:
            height = h * len(items) + h
        w1 = w2 = 0
        for item in items:
            item.enabled = self.command_is_enabled(item, focus)
            w1 = max(w1, font.size(item.text)[0])
            w2 = max(w2, font.size(item.keyname)[0])
        width = w1 + 2 * margin
        self._key_margin = width
        if w2 > 0:
            width += w2 + margin
        if self.scrolling:
            width += self.scroll_button_size
        self.size = (width, height)
        self._hilited = None

        self.rect.clamp_ip(self.root.rect)

        return Dialog.present(self, centered=False)

    @staticmethod
    def command_is_enabled(item, focus):
        cmd = item.command
        if cmd:
            enabler_name = cmd + '_enabled'
            handler = focus
            while handler:
                enabler = getattr(handler, enabler_name, None)
                if enabler:
                    return enabler()
                handler = handler.next_handler()
        return True

    def scroll_up_rect(self):
        d = self.scroll_button_size
        r = Rect(0, 0, d, d)
        m = self.margin
        r.top = m
        r.right = self.width - m
        r.inflate_ip(-4, -4)
        return r

    def scroll_down_rect(self):
        d = self.scroll_button_size
        r = Rect(0, 0, d, d)
        m = self.margin
        r.bottom = self.height - m
        r.right = self.width - m
        r.inflate_ip(-4, -4)
        return r

    def draw(self, surf):
        font = self.font
        h = font.get_linesize()
        sep = surf.get_rect()
        sep.height = 1
        if self.scrolling:
            sep.width -= self.margin + self.scroll_button_size
        colors = [self.disabled_color, self.fg_color]
        bg = self.bg_color
        xt = self.margin
        xk = self._key_margin
        y = h // 2
        hilited = self._hilited
        if self.scrolling:
            items = self._items[self.scroll:self.scroll + self.scroll_items]
        else:
            items = self._items
        for item in items:
            text = item.text
            if not text:
                sep.top = y + h // 2
                surf.fill(colors[0], sep)
            else:
                if item is hilited:
                    rect = surf.get_rect()
                    rect.top = y
                    rect.height = h
                    if self.scrolling:
                        rect.width -= xt + self.scroll_button_size
                    surf.fill(colors[1], rect)
                    color = bg
                else:
                    color = colors[item.enabled]
                buf = font.render(item.text, True, color)
                surf.blit(buf, (xt, y))
                keyname = item.keyname
                if keyname:
                    buf = font.render(keyname, True, color)
                    surf.blit(buf, (xk, y))
            y += h
        if self.scrolling:
            if self.can_scroll_up():
                self.draw_scroll_up_button(surf)
            if self.can_scroll_down():
                self.draw_scroll_down_button(surf)

    def draw_scroll_up_button(self, surface):
        r = self.scroll_up_rect()
        c = self.scroll_button_color
        draw.polygon(surface, c, [r.bottomleft, r.midtop, r.bottomright])

    def draw_scroll_down_button(self, surface):
        r = self.scroll_down_rect()
        c = self.scroll_button_color
        draw.polygon(surface, c, [r.topleft, r.midbottom, r.topright])

    def mouse_move(self, e):
        self.mouse_drag(e)

    def mouse_drag(self, e):
        item = self.find_enabled_item(e)
        if item is not self._hilited:
            self._hilited = item
            self.invalidate()

            if item:
                self._selected_item_index = self._items.index(item)

    def mouse_up(self, e):
        if 1 <= e.button <= 3:
            item = self.find_enabled_item(e)
            if item:
                self.dismiss(self._items.index(item))

    def find_enabled_item(self, e):
        x, y = e.local
        if 0 <= x < (self.width - self.margin - self.scroll_button_size
                     if self.scrolling else self.width):
            h = self.font.get_linesize()
            i = (y - h // 2) // h + self.scroll
            items = self._items
            if 0 <= i < len(items):
                item = items[i]
                if item.enabled:
                    return item

    def mouse_down(self, event):
        if event.button == 1:
            if self.scrolling:
                p = event.local
                if self.scroll_up_rect().collidepoint(p):
                    self.scroll_up()
                    return
                elif self.scroll_down_rect().collidepoint(p):
                    self.scroll_down()
                    return
        if event.button == 4:
            self.scroll_up()
        if event.button == 5:
            self.scroll_down()

        Dialog.mouse_down(self, event)

    def scroll_up(self):
        if self.can_scroll_up():
            self.scroll = max(self.scroll - self.scroll_page, 0)

    def scroll_down(self):
        if self.can_scroll_down():
            self.scroll = min(self.scroll + self.scroll_page,
                              len(self._items) - self.scroll_items)

    def can_scroll_up(self):
        return self.scrolling and self.scroll > 0

    def can_scroll_down(self):
        return (self.scrolling and
                self.scroll + self.scroll_items < len(self._items))

    def find_item_for_key(self, e):
        for item in self._items:
            if item.keycode == e.key \
                    and item.shift == e.shift and item.alt == e.alt:
                focus = get_focus()
                if self.command_is_enabled(item, focus):
                    return self._items.index(item)
                else:
                    return -1
        return -1

    def get_command(self, i):
        if i >= 0:
            item = self._items[i]
            cmd = item.command
            if cmd:
                return cmd + '_cmd'

    def invoke_item(self, i):
        cmd = self.get_command(i)
        if cmd:
            get_focus().handle_command(cmd)

    def key_down(self, event):
        """Handles key presses to select and activate menu items or dismiss it.
        :param event: object: The event to be processed."""
        key = self.root.getKey(event)
        last_index = len(self._items) - 1
        # Just define a dummy method when scrolling is not necessary.
        def _x(*args, **kwargs):
            """..."""
            pass

        def page_up_down(*args, **kwargs):
            """Triggers scroll alignment to see the selected item on page up/down event."""
            self.scroll = min(self._selected_item_index, last_index - self.scroll_items + 1)

        view_meth = _x
        if key == "Up":
            if self._selected_item_index == 0:
                self._selected_item_index = last_index
                self.scroll = last_index - self.scroll_items + 1
            else:
                self._selected_item_index -= 1
                view_meth = self.scroll_up
        elif key == "Down":
            if self._selected_item_index == last_index:
                self._selected_item_index = 0
                self.scroll = 0
            else:
                self._selected_item_index += 1
                view_meth = self.scroll_down
        elif key == "Page up":
            self._selected_item_index = max(0, self._selected_item_index - self.scroll_items)
            view_meth = page_up_down
        elif key == "Page down":
            self._selected_item_index = min(last_index, self._selected_item_index + self.scroll_items)
            view_meth = page_up_down
        elif key in ("Return", "Enter", "Space"):
            self.dismiss(self._selected_item_index)
        elif key == "Escape":
            self.dismiss(False)
        else:
            Dialog.key_down(self, event)

        self._hilited = self._items[self._selected_item_index]

        # Ensure the selected item is visible by scrollign accordingly
        if self._hilited not in self._items[self.scroll:self.scroll + self.scroll_items]:
            view_meth()
