# Sketch - A Python-based interactive drawing program
# Copyright (C) 1998, 1999 by Bernhard Herzog
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library 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
# Library General Public License for more details.
#
# You should have received a copy of the GNU Library 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

#
#	The Clone Object
#

#from traceback import print_stack

from Sketch import Translation, NullPoint, Trafo
from Sketch import PointType, Undo, NullUndo
from Sketch.const import CHANGED
from base import Bounded, HierarchyNode



_clone_registry = {}

def _register_clone(original):
    original = id(original)
    _clone_registry[original] = _clone_registry.get(original, 0) + 1

def _unregister_clone(original):
    original = id(original)
    _clone_registry[original]= _clone_registry[original] - 1
    if _clone_registry[original] == 0:
	del _clone_registry[original]

class Clone(Bounded, HierarchyNode):

    is_Clone = 1

    def __init__(self, original = None, duplicate = None):
	HierarchyNode.__init__(self, duplicate = duplicate)
	if original is not None and original.is_Clone:
	    duplicate = original
	    original = None
	if duplicate is not None:
	    self._original = duplicate._original
	    self._center = duplicate._center
	    self._offset = duplicate._offset
	else:
	    self._original = original
	    self._center = self._original.coord_rect.center()
	    self._offset = NullPoint
	self.register()

    def register(self):
	_register_clone(self._original)
	self._original.Subscribe(CHANGED, self.orig_changed)

    def unregister(self):
	self._original.Unsubscribe(CHANGED, self.orig_changed)
	_unregister_clone(self._original)

    def __getattr__(self, attr):
	if self._lazy_attrs.has_key(attr):
	    return Bounded.__getattr__(self, attr)
	#print 'Clone.__getattr__: from original:', attr
	#if attr in ('__nonzero__', 'document'):
	#    print_stack()
	return getattr(self._original, attr)

    def update_rects(self):
	off = self._offset
	self.bounding_rect = self._original.bounding_rect.translated(off)
	self.coord_rect = self._original.coord_rect.translated(off)

    def Translate(self, offset):
	self._offset = self._offset + offset
	self.del_lazy_attrs()
	return self.Translate, -offset

    def _set_offset(self, offset):
	undo = (self._set_offset, self._offset)
	self._offset = offset
	self.del_lazy_attrs()
	return undo

    def offset_center(self, offset):
	undo = (self.offset_center, -offset)
	self._center = self._center + offset
	return undo

    def Transform(self, trafo):
	center = self._center + self._offset
	offset = trafo(center) - center
	if self.document is not None:
	    self.document.AddAfterHandler(_transform,
					  (self._original, trafo.matrix()), -1)
	return self.offset_center(offset)

    def orig_changed(self, *args):
	if self.document is not None:
	    self.document.AddClearRect(self.bounding_rect)
	self.del_lazy_attrs()
	center = self._center
	self._center = self._original.coord_rect.center()
	self._offset = self._offset + center - self._center
	if self.document is not None:
	    self.document.AddClearRect(self.bounding_rect)

    def DrawShape(self, device, rect = None):
	device.PushTrafo()
	try:
	    device.Translate(self._offset)
	    self._original.DrawShape(device, rect)
	finally:
	    device.PopTrafo()

    def Hit(self, p, rect, device):
	off = -self._offset
	return self._original.Hit(p + off, rect.translated(off), device)


    def Info(self):
	return 'Clone of ' + self._original.Info()


    # overwrite Selectable methods
    def SelectSubobject(self, p, rect, device, path = None, *rest):
	return self

    def GetObjectHandle(self, multiple):
	trafo = Translation(self._offset)
	handle = self._original.GetObjectHandle(multiple)
	if type(handle) == PointType:
	    return trafo(handle)
	else:
	    return map(trafo, handle)

    # overwrite Bounded methods
    def LayoutPoint(self):
	return self._original.LayoutPoint() + self._offset

    def GetSnapPoints(self):
	return map(Translation(self._offset), self._original.GetSnapPoints())


def _transform(original, matrix):
    trafo = apply(Trafo, matrix)
    undo = original.Transform(trafo)
    doc = original.document
    doc.AddUndo((_undo, doc, [undo]))

def _after_handler(undo, list):
    list[0] = Undo(undo)

def _undo(doc, undo):
    list = [NullUndo]
    doc.AddAfterHandler(_after_handler, (undo[0], list), -1)
    return (_undo, doc, list)

def CreateClone(object):
    clone = Clone(object)
    undo = object.parent.ReplaceChild(object, clone)
    return clone.Duplicate(), undo
