#!/usr/bin/env python

# Copyright (C) 2010-2012  Sam Bull

"""
Menu widget. Creates a menu for a game.

"""

from _locals import *
from boxes import VBox
from scroll_box import ScrollBox
from . import *

class Menu(Simple):

    """
    Menu

    Can be indexed to access widgets by name.

    Attributes:
      func_dict: Assign a lambda to return a dictionary of functions for
          config file to utilise.

    """

    _modal = True
    _layered = True
    _settings_default = {"offset": (100, 50), "col": (0,0,1), "apply": False}

    _menus = []
    _dict = {}  # Dict for indexing widgets
    _old_menu = None
    _curr_menu = 0
    func_dict = lambda self: {}

    def _config(self, **kwargs):
        """
          menu: Either tuple containing menu data, to be documented.
              Or file object to read config data from, in the same format.
          apply: TODO ``bool`` True if an apply button should be added.
          col: ``tuple`` (r,g,b), Colour used for the background.
          offset: ``tuple`` (x,y) Contains position of menu widgets. y is
              added to bottom of title.

        """
        if "apply" in kwargs:
            self._settings["apply"] = kwargs["apply"]
        if "col" in kwargs:
            self._settings["col"] = kwargs["col"]
        if "offset" in kwargs:
            self._settings["offset"] = kwargs["offset"]
        if "menu" in kwargs:
            self._menus = []  # Remove previous menus
            self._dict = {}
            # Grab function dictionary
            self._funcs = self.func_dict()

            # Set size to screen size
            if not hasattr(self, "image"):
                self._create_base_images(get_screen().rect.size)

            menu = kwargs["menu"]
            # If file, read config tuple
            if isinstance(menu, file):
                menu_data = "".join(menu.readlines())
                menu = eval(menu_data)
                assert isinstance(menu, tuple)

            menu_data = [(menu, None)]  # (data, parent)
            # Create each submenu, by iterating through the menu data
            while menu_data:
                self._menus.append(_SubMenu(self.rect.size,
                                            col=self._settings["col"]))
                self._config_menu(menu_data, self._menus[-1])

    def _config_menu(self, data_queue, menu):
        """
        Configure the passed in menu, using the information from the first
        item in data_queue.

        New sub-menus discovered in the data will be appended to the data_queue
        for later processing.

        """
        data, parent = data_queue.pop(0)
        widgets = []
        # Parse menu data
        for item in data:
            # Title
            if isinstance(item, str):
                menu._title = Simple(Font["title"].render(item[2:], True,
                                                          Font.col))
                menu._title.rect.midtop = (self.rect.centerx, 40)
            # Sub-menu
            elif item[0].startswith("m:"):
                data_queue.append((item, len(self._menus)-1))
                surf = Font["widget"].render(item[0][2:], True, Font.col)
                widgets.append(Button(surf))
                # Change menu on button activate
                num = len(self._menus)-1 + len(data_queue)
                widgets[-1].activate = lambda n=num: self.change_menu(n)
            # Category/divider
            elif item[0].startswith("c:"):
                div = Simple(Font["widget"].render(item[0][2:], True,
                                                   Font.col))
                self.get_draw().line(div.image, Font.col, (0, div.rect.h-1),
                                     (div.rect.w, div.rect.h-1))
                widgets.append(div)
            # Widget
            elif item[0].startswith("w:"):
                args = self._get_args(item[1:])
                name = args.pop("name")
                f = args.pop("func") if ("func" in args) else None
                if item[0].endswith("input_box"):
                    widget = input_box.InputBox(**args)
                elif item[0].endswith("button"):
                    for key in args:
                        if key == "surf":
                            args[key] = eval(args[key])
                    widget = button.Button(**args)
                elif item[0].endswith("label"):
                    widget = label.Label(**args)
                self._dict[name] = widget
                if f: widget.activate = self._funcs[f]
                widgets.append(widget)
            # Function
            elif item[0].startswith("f:"):
                surf = Font["widget"].render(item[1], True, Font.col)
                widgets.append(button.Button(surf=surf))
                widgets[-1].activate = self._funcs[item[0][2:]]

        # Draw a back menu item
        if parent is not None:
            surf = Font["widget"].render("Back", True, Font.col)
            widgets.append(button.Button(surf=surf))
            widgets[-1].activate = lambda n=parent: self.change_menu(n)

        menu._widgets = tuple(widgets)

    def _draw(self, draw):
        for menu in self._menus:
            # Pack all widgets into a VBox
            box = VBox(widgets=menu._widgets, spacing=15)
            pos = (self._settings["offset"][0],
                   self._settings["offset"][1] + menu._title.rect.bottom)
            box = ScrollBox((self.rect.w - pos[0], self.rect.h - pos[1]),
                            widget=box, pos=pos)
            menu.config(col=self._settings["col"], menu=box)

    def change_menu(self, menu_num):
        """
        Change the currently displayed menu.

        Args:
          menu_num: ``int`` The number representing the menu.

        """
        self._old_menu = self._curr_menu
        self._curr_menu = menu_num
        self._menus[self._curr_menu]._fade = 0

    def update(self, time):
        menu = self._menus[self._curr_menu]
        menu.update(time)
        if self._old_menu is not None:
            self.image.blit(self._menus[self._old_menu].image, (0,0))
            menu.image.set_alpha(menu._fade)
            menu._fade += time / 3.
            if menu._fade >= 255:
                menu._fade = None
                self._old_menu = None
                menu.image.set_alpha(255)
        self.image.blit(menu.image, (0,0))

    def _event(self, event):
        self._menus[self._curr_menu]._event(event)

    def _get_args(self, args):
        """Get the arguments passed in, saving them into a dictionary."""
        return {arg.split("=")[0]: arg.split("=")[1] for arg in args}

    def __getitem__(self, key):
        """Return widgets by name."""
        return self._dict[key]

class _SubMenu(Simple):

    """
    A single menu object to be created and managed by the Menu class.

    """
    _settings_default = {"col": (0,0,1), "menu": None}

    _title = None
    _widgets = ()

    def _config(self, **kwargs):
        if "col" in kwargs:
            self._settings["col"] = kwargs["col"]
        if "menu" in kwargs:
            self._settings["menu"] = kwargs["menu"]

    def _draw(self, draw):
        self._images["image"].fill(self._settings["col"])
        if self._title:
            self._images["image"].blit(self._title.image, self._title.pos)

    def update(self, time):
        self.image = self._images["image"].copy()
        self._settings["menu"].update(time)
        self.image.blit(self._settings["menu"].image,self._settings["menu"].pos)

    def _event(self, event):
        """Send events to container."""
        self._settings["menu"]._event(event)