# -*- mode: python; coding: utf-8 -*-
#
# Pigment Python tools
#
# Copyright © 2006, 2007, 2008 Fluendo Embedded S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

from pgm.utils import classinit
import pgm
import math
import gobject

class Group(gobject.GObject):

    __metaclass__ = classinit.GClassInitMeta
    __classinit__ = classinit.build_properties
    
    __gsignals__ = {
        'drag-begin': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'drag-motion': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'drag-end': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'clicked': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'pressed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'released': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
        'double-clicked': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN,
                (gobject.TYPE_FLOAT, gobject.TYPE_FLOAT, gobject.TYPE_FLOAT,
                pgm.ButtonType, gobject.TYPE_UINT)),
    }

    def __init__(self, canvas=None, layer=pgm.DRAWABLE_MIDDLE):
        super(Group, self).__init__()
        self._children = []
        self._position = (0.0, 0.0, 0.0)
        self._opacity = 255
        self._screen_visible = False
        self._real_visible = False
        self._parent = None
        self._canvas = canvas
        self._layer = layer

    def regenerate(self):
        for child in self._children:
            child.regenerate()

    def children__get(self):
        return self._children


    def position__get(self):
        return self._position


    def position__set(self, value):
        self._position = value
        for drawable in self._children:
            if not isinstance(drawable, Group):
                drawable.position_offset = self.absolute_position
            drawable.position = drawable.position


    def x__get(self):
        return self._position[0]


    def x__set(self, value):
        new_position = (value, self._position[1], self._position[2])
        self.position = new_position


    def y__get(self):
        return self._position[1]


    def y__set(self, value):
        new_position = (self._position[0], value, self._position[2])
        self.position = new_position


    def z__get(self):
        return self._position[2]


    def z__set(self, value):
        new_position = (self._position[0], self._position[1], value)
        self.position = new_position


    def absolute_position__get(self):
        if self._parent == None:
            return self._position
        else:
            absolute_position = tuple(map(lambda x,y: x+y,
                                               self._position,
                                               self._parent.absolute_position))
            return absolute_position

    def opacity__get(self):
            return self._opacity

    def opacity__set(self, value):
        factor = float(value) / 255
        self._opacity = value
        for drawable in self._children:
            if isinstance (drawable, Group):
                drawable.opacity = drawable.opacity
            else:
                drawable.opacity_factor_offset__set(self.absolute_opacity_factor__get())

    def absolute_opacity_factor__get(self):
        if self._parent == None:
            return self._opacity / 255.0
        else:
            opacity_factor = self._opacity * self._parent.absolute_opacity_factor__get() / 255.0
            return opacity_factor


    def _set_visibility_from_parent(self, visible, from_parent):

        if not from_parent:
            self._real_visible = visible

        if self._parent == None:
            self._real_visible = visible
            self._screen_visible = visible
        elif self._parent.visible == True and self._real_visible == True:
            self._screen_visible = True
        else:
            self._screen_visible = False


        for drawable in self._children:
            if isinstance(drawable, Group):
                drawable._set_visibility_from_parent(self._screen_visible, True)
            else:
                drawable.parent_visibility = self._screen_visible


    def visible__get(self):
        return self._screen_visible


    def visible__set(self, value):
        self._set_visibility_from_parent(value, False)

    def size__set(self, size):
        current_size = self.size

        if current_size[0] == 0.0 or current_size[1] == 0.0:
            # FIXME: do something more sensible
            return

        kx, ky = (size[0]/current_size[0], size[1]/current_size[1])

        for child in self._children:
            child.x *= kx
            child.y *= ky
            w, h = child.size
            child.size = (w*kx, h*ky)

    def width__set(self, width):
        height = self.size[1]
        self.size = (width, height)

    def height__set(self, height):
        width = self.size[0]
        self.size = (width, height)

    def size__get(self):
        if len(self._children) == 0:
            return (0.0, 0.0)

        child = self._children[0]
        min_x = child.x
        min_y = child.y
        max_x = child.x + child.size[0]
        max_y = child.y + child.size[1]
        for child in self._children:
             min_x = min(min_x, child.x)
             min_y = min(min_y, child.y)
             max_x = max(max_x, child.x + child.size[0])
             max_y = max(max_y, child.y + child.size[1])

        return (max_x-min_x, max_y-min_y)

    def width__get(self):
        return self.size[0]

    def height__get(self):
        return self.size[1]

    def parent__get(self):
        return self._parent

    def add(self, drawable, forward_signals=False):
        if drawable not in self._children:
            self._children.append(drawable)
            if isinstance(drawable, Group):
                drawable._parent = self
                drawable.position = drawable.position
                drawable.opacity = drawable.opacity
                drawable.canvas = self._canvas
            else:
                if self._canvas != None:
                    self._canvas.add(self._layer, drawable)
                drawable.position_offset = self.absolute_position
                drawable.parent_visibility = self.visible
                drawable.opacity_factor_offset__set(self.absolute_opacity_factor__get())

            if forward_signals:
                for signal_name in ('clicked', 'pressed', 'released',
                                    'drag-begin', 'drag-motion', 'drag-end'):
                    drawable.connect(signal_name,
                            self._proxy_child_signal, signal_name) 

    def remove(self, drawable):
        if drawable in self._children:
            self._children.remove(drawable)
            if isinstance(drawable, Group):
                drawable._parent = None
                drawable.canvas = None
            else:
                if self._canvas != None:
                    self._canvas.remove(drawable)

    def empty(self):
        for child in self._children:
            if isinstance(child, Group):
                child.empty()
            else:
                self._canvas.remove(child)

        self._children = []

    def canvas__set(self, canvas):
        for child in self._children:
            if isinstance(child, Group):
                child.canvas = canvas
            else:
                if canvas != None:
                    canvas.add(self._layer, child)
                elif self._canvas != None:
                    self._canvas.remove(child)

        self._canvas = canvas

    def canvas__get(self):
        return self._canvas

    def _proxy_child_signal(self, drawable, x, y, z, button, time, signal_name):
        return self.emit(signal_name, x, y, z, button, time)
