# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.

import gobject
import pgm
import weakref
from elisa.plugins.pigment.animation.implicit import AnimatedObject

class Node(gobject.GObject):

    def __init__(self):
        super(Node, self).__init__()
        self._parent = None

        self._relative_visible = False
        self.absolute_visible = self._relative_visible
        self._relative_x = 0.0
        self._relative_y = 0.0
        self._relative_z = 0.0
        self._relative_width = 1.0
        self._relative_height = 1.0
        self._relative_opacity = 255
        self.absolute_opacity = self._relative_opacity
        self.absolute_size = (1.0, 1.0)
        self.absolute_position = (0.0, 0.0, 0.0)

    def absolute_width__get(self):
        return self.absolute_size[0]

    def absolute_width__set(self, value):
        self.absolute_size = (value, self.absolute_size[1])

    absolute_width = property(absolute_width__get, absolute_width__set)

    def absolute_height__get(self):
        return self.absolute_size[1]

    def absolute_height__set(self, value):
        self.absolute_size = (self.absolute_size[0], value)

    absolute_height = property(absolute_height__get, absolute_height__set)

    def absolute_x__get(self):
        return self.absolute_position[0]

    def absolute_x__set(self, value):
        self.absolute_position = (value, self.absolute_position[1],
                                  self.absolute_position[2])

    absolute_x = property(absolute_x__get, absolute_x__set)

    def absolute_y__get(self):
        return self.absolute_position[1]

    def absolute_y__set(self, value):
        self.absolute_position = (self.absolute_position[0], value,
                                  self.absolute_position[2])

    absolute_y = property(absolute_y__get, absolute_y__set)

    def absolute_z__get(self):
        return self.absolute_position[2]

    def absolute_z__set(self, value):
        self.absolute_position = (self.absolute_position[0],
                                  self.absolute_position[1], value)

    absolute_z = property(absolute_z__get, absolute_z__set)

    def parent__set(self, parent):
        if parent != None:
            self._parent = weakref.ref(parent)

            # update absolute properties using current relative values
            self.update_absolute_size(parent)
            self.update_absolute_position(parent)
            self.update_absolute_opacity(parent)
            self.update_absolute_visible(parent)
        else:
            self._parent = None
            self.absolute_visible = self._relative_visible

    def parent__get(self):
        try:
            return self._parent()
        except TypeError:
            return None

    parent = property(parent__get, parent__set)


    # x, y and z properties

    def x__get(self):
        return self._relative_x

    def x__set(self, value):
        if value == self._relative_x:
            return
        self._relative_x = value
        parent = self.parent
        if parent:
            self.update_absolute_x(parent)
        else:
            self.set_absolute_x(value)

    x = property(x__get, x__set)

    def set_absolute_x(self, value):
        self.absolute_x = value

    def update_absolute_x(self, parent):
        self.absolute_x = parent.absolute_x + \
                          self._relative_x * parent.absolute_width

    def y__get(self):
        return self._relative_y

    def y__set(self, value):
        if value == self._relative_y:
            return
        self._relative_y = value
        parent = self.parent
        if parent:
            self.update_absolute_y(parent)
        else:
            self.set_absolute_y(value)

    y = property(y__get, y__set)

    def set_absolute_y(self, value):
        self.absolute_y = value

    def update_absolute_y(self, parent):
        self.absolute_y = parent.absolute_y + \
                          self._relative_y*parent.absolute_height

    # FIXME: write tests for z property
    def z__get(self):
        return self._relative_z

    def z__set(self, value):
        if value == self._relative_z:
            return
        self._relative_z = value
        parent = self.parent
        if parent:
            self.update_absolute_z(parent)
        else:
            self.set_absolute_z(value)

    z = property(z__get, z__set)

    def set_absolute_z(self, value):
        self.absolute_z = value

    def update_absolute_z(self, parent):
        self.absolute_z = parent.absolute_z + self._relative_z


    # Position property

    def position__get(self):
        return (self._relative_x, self._relative_y, self._relative_z)

    def position__set(self, position):
        if position == (self._relative_x, self._relative_y, self._relative_z):
            return
        self._relative_x = position[0]
        self._relative_y = position[1]
        self._relative_z = position[2]
        parent = self.parent
        if parent:
            self.update_absolute_position(parent)
        else:
            self.set_absolute_position(position)

    position = property(position__get, position__set)

    def set_absolute_position(self, value):
        self.absolute_position = value

    def update_absolute_position(self, parent):
        parent_position = parent.absolute_position
        parent_size = parent.absolute_size
        self.absolute_position = (
                    parent_position[0] + self._relative_x * parent_size[0],
                    parent_position[1] + self._relative_y * parent_size[1],
                    parent_position[2] + self._relative_z)


    # width and height properties

    def width__get(self):
        return self._relative_width

    def width__set(self, value):
        if value == self._relative_width:
            return
        self._relative_width = value
        parent = self.parent
        if parent:
            self.update_absolute_width(parent)
        else:
            self.set_absolute_width(value)

    width = property(width__get, width__set)

    def set_absolute_width(self, value):
        self.absolute_width = value

    def update_absolute_width(self, parent):
        self.absolute_width = self._relative_width*parent.absolute_width

        # FIXME: write tests before uncommenting
        #self.update_absolute_x()


    def height__get(self):
        return self._relative_height

    def height__set(self, value):
        if value == self._relative_height:
            return
        self._relative_height = value
        parent = self.parent
        if parent:
            self.update_absolute_height(parent)
        else:
            self.set_absolute_height(value)

    height = property(height__get, height__set)

    def set_absolute_height(self, value):
        self.absolute_height = value

    def update_absolute_height(self, parent):
        self.absolute_height = self._relative_height*parent.absolute_height

        # FIXME: write tests before uncommenting
        #self.update_absolute_y()


    # Size property

    def size__get(self):
        return self._relative_width, self._relative_height

    def size__set(self, size):
        if size == (self._relative_width, self._relative_height):
            return
        self._relative_width = size[0]
        self._relative_height = size[1]
        parent = self.parent
        if parent:
            self.update_absolute_size(parent)
        else:
            self.set_absolute_size(size)

    size = property(size__get, size__set)

    def set_absolute_size(self, value):
        self.absolute_size = value

    def update_absolute_size(self, parent):
        parent_size = parent.absolute_size
        self.absolute_size = (self._relative_width * parent_size[0],
                              self._relative_height * parent_size[1])

    # Visible

    def visible__get(self):
        return self._relative_visible

    def visible__set(self, value):
        if value == self._relative_visible:
            return
        self._relative_visible = value
        parent = self.parent
        if parent:
            self.update_absolute_visible(parent)
        else:
            self.set_absolute_visible(value)

    visible = property(visible__get, visible__set)

    def set_absolute_visible(self, value):
        self.absolute_visible = value

    def update_absolute_visible(self, parent):
        self.absolute_visible = self._relative_visible and \
                                parent.absolute_visible
        # implicit animation object is set to act as a passthrough, that is
        # disabled and not animating, when the widget is invisible
        if hasattr(self, "_animated"):
            self._animated.passthrough = not self.absolute_visible


     # Opacity

     # FIXME: write tests for opacity
    def opacity__get(self):
        return self._relative_opacity

    def opacity__set(self, value):
        if value == self._relative_opacity:
            return
        self._relative_opacity = value
        parent = self.parent
        if parent:
            self.update_absolute_opacity(parent)
        else:
            self.set_absolute_opacity(value)

    opacity = property(opacity__get, opacity__set)

    def set_absolute_opacity(self, value):
        self.absolute_opacity = value

    def update_absolute_opacity(self, parent):
        self.absolute_opacity = \
                int(self._relative_opacity*parent.absolute_opacity/255.0)


    def clean(self):
        if hasattr(self, "_animated"):
            # FIXME: write and call clean function on the implicit animation object
            # releasing references to the widget
            self._animated.stop_animations()
            self._animated.object = None
            self._animated = None

        if self.parent:
            self.parent.remove(self)
            self.parent = None

    def animated__get(self):
        try:
            return self._animated
        except AttributeError:
            self._animated = AnimatedObject(self)
            # implicit animation object is set to act as a passthrough, that is
            # disabled and not animating, when the widget is invisible
            self._animated.passthrough = not self.absolute_visible
            return self._animated

    animated = property(animated__get)
