#!/usr/bin/env python

# Copyright (C) 2010-2012  Sam Bull, Michael Rochester

"""
Label to display information to the user.

"""

import pygame.mouse
from pygame.locals import *

from _locals import *
from base_widget import Simple

class Label(Simple):

    """
    Label

    Attributes:
      text: ``str`` displayed in label. Can be assigned as a shortcut for
          ``config(label=)`` with no second paramenter.
    """

    _settings_default = {"text": "", "col": Font.col, "font": Font["widget"],
                         "col_selection": (118, 45, 215)}

    _over = False
    _select = None  # Starting point of selection
    __cursor_pos = 0

    def _config(self, **kwargs):
        """
          text: Either ``str`` containing text to be displayed or
              ``tuple`` containing two strings. First string is text to
              be displayed, second string is rect attribute to be used
              for position. Defaults to 'topleft' if not passing a tuple.
          col: ``tuple`` (r,g,b) Text colour.
          font: Font object the label will render with.
          selectable: ``bool`` True if the text should be selectable.
          col_selection: ``tuple`` (r,g,b) Colour of selection rectangle.

        """
        if "init" in kwargs:
            strings = pygame.cursors.textmarker_strings
            cursor = pygame.cursors.compile(strings)
            size = (len(strings[0]), len(strings))
            hotspot = (size[0]/2, size[1]/2)
            self._cursor = (size, hotspot) + cursor
        if "text" in kwargs:
            if isinstance(kwargs["text"], str):
                self._settings["text"] = kwargs["text"]
            else:
                self._settings["text"] = kwargs["text"][0]
                self._temp_pos = kwargs["text"][1]
        if "col" in kwargs:
            self._settings["col"] = kwargs["col"]
        if "font" in kwargs:
            self._settings["font"] = kwargs["font"]
        if "selectable" in kwargs:
            self._can_focus = kwargs["selectable"]
        if "col_selection" in kwargs:
            self._settings["col_selection"] = kwargs["col_selection"]
        
    def _draw(self, draw):
        if hasattr(self, "_temp_pos"):
            pos = getattr(self.rect, self._temp_pos)

        # Split into lines
        text = []
        for line in self._settings["text"].split("\n"):
            text.append(self._settings["font"].render(line, True,
                                                      self._settings["col"]))

        # Dynamically set size
        h = 0
        for line in text:
            h += line.get_size()[1]
        w = max(text, key=lambda x: x.get_size()[0])
        self._create_base_images((w.get_size()[0], h))

        # Blit each line
        y = 0
        for line in text:
            self._images["image"].blit(line, (0,y))
            y += line.get_size()[1]

        # Copy position attribute over
        if hasattr(self, "_temp_pos"):
            setattr(self.rect, self._temp_pos, pos)
            del self._temp_pos

        self.image = self._images["image"].copy()

        # Store as tuple of (pos, width) tuples.
        if self._can_focus:
            chars = []
            p = 0
            for c in range(1, len(self._settings["text"])+1):
                char = self._settings["font"].render(self._settings["text"][:c],
                                                     True, (0,0,0))
                chars.append((p, char.get_size()[0] - p))
                p = char.get_size()[0]
            chars.append((p, 0))
            self._chars = tuple(chars)

    def _event(self, event):
        if event.type == MOUSEBUTTONDOWN and event.button == 1:
            # Begin drawing selection
            if pygame.key.get_mods() & KMOD_SHIFT and self._select is None:
                self._select = self._cursor_pos
            self._cursor_pos = self._mouse_cursor(event.pos)
            if not pygame.key.get_mods() & KMOD_SHIFT:
                self._select = self._cursor_pos
        elif event.type == MOUSEMOTION and event.buttons[0]:
            # Continue drawing selection while mouse held down
            self._cursor_pos = self._mouse_cursor(event.pos)
        elif event.type == MOUSEBUTTONUP:
            # Set cursor position with mouse click
            self._cursor_pos = self._mouse_cursor(event.pos)
            if self._select == self._cursor_pos:
                self._select = None
        elif event.type == KEYDOWN:
            if event.key == K_ESCAPE:
                self._select = None
            elif event.key == K_LEFT:
                if not event.mod & KMOD_SHIFT:
                    self._select = None  # Break selection
                elif self._select is None:
                    # Reset selection if not selecting
                    self._select = self._cursor_pos
                self._cursor_pos -= 1
                # Remove selection when cursor is at same position
                if self._select == self._cursor_pos:
                    self._select = None
            elif event.key == K_RIGHT:
                if not event.mod & KMOD_SHIFT:
                    self._select = None  # Break selection
                elif self._select is None:
                    self._select = self._cursor_pos
                self._cursor_pos += 1
                if self._select == self._cursor_pos:
                    self._select = None
            elif event.mod & KMOD_CTRL:
                if event.key == K_a:  # Select all
                    self._select = 0
                    self._cursor_pos = len(self._settings["text"])
                elif event.key == K_c and self._select is not None:  # Copy
                    select = self._select_fix()
                    string = "".join(
                        self._settings["text"][select[0]:select[1]])
                    try:
                        pygame.scrap.put(SCRAP_TEXT, string)
                    except pygame.error:
                        print "Please run 'pygame.scrap.init()'" \
                              " to use the clipboard."

    def update(self, time):
        if self._can_focus:
            # Change cursor when mouse not held down
            if not pygame.mouse.get_pressed()[0]:
                if not self._over and \
                  self.rect_abs.collidepoint(pygame.mouse.get_pos()):
                    self._over = True
                    self._set_cursor(*self._cursor)
                elif self._over and \
                  not self.rect_abs.collidepoint(pygame.mouse.get_pos()):
                    self._over = False
                    self._remove_cursor()
            if self.has_focus():
                self.image = self._images["image"].copy()
                draw = self.get_draw()
                if self._select is None:
                    # Draw cursor in box
                    x = self._chars[self._cursor_pos][0] - 1
                    draw.line(self.image, (0,0,1), (x, 2), (x, self.rect.h-2))
                else:
                    select = self._select_fix()
                    # Semi-transparent selection rectangle
                    w = (self._chars[select[1]][0] - self._chars[select[0]][0])
                    selection = Simple((w, self.rect.h - 2))
                    selection.pos = (self._chars[select[0]][0], 1)
                    selection.image.fill(self._settings["col_selection"])
                    selection.image.set_alpha(100)
                    # Border around selection rectangle
                    selection_b = Simple((selection.rect.w+2,
                                          selection.rect.h+2))
                    draw.rect(selection_b.image,
                              self._settings["col_selection"],
                              selection_b.rect, 1)
                    self.image.blit(selection.image, selection.pos)
                    self.image.blit(selection_b.image, (selection.rect.x-1,
                                                        selection.rect.y-1))

    @property
    def text(self):
        return self._settings["text"]
    @text.setter
    def text(self, value):
        self._settings["text"] = value
        self._draw()

    def _mouse_cursor(self, mouse_pos):
        """Return the text cursor position of the mouse."""
        pos = mouse_pos[0] - self.rect_abs.x
        for index, (p,w) in enumerate(self._chars):
            if pos <= p + w/2:
                break
        return index

    def _select_fix(self):
        """If selection is right-to-left then reverse positions."""
        if self._select > self._cursor_pos:
            return (self._cursor_pos, self._select)
        else:
            return (self._select, self._cursor_pos)

    def _focus_exit(self):
        """Cancel any selection when focus is lost."""
        self.image = self._images["image"].copy()

    @property
    def _cursor_pos(self):
        return self.__cursor_pos
    @_cursor_pos.setter
    def _cursor_pos(self, value):
        # Keep cursor position within text
        self.__cursor_pos = min(max(value, 0), len(self._settings["text"]))