# Sketch - A Python-based interactive drawing program
# Copyright (C) 1998, 1999, 2001 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


from types import TupleType
from Sketch import _, SketchError, CreateListUndo, Undo, \
     Trafo, Translation, Scale, Identity, TrafoType

from compound import Compound


class PluginCompound(Compound):

    class_name = ''	 # has to be provided by derived classes
    is_Plugin = 1

    def SetParameters(self, kw = None):
        # XXX could be extended to handle keyword arguments.
        undo = {}
        for key, value in kw.items():
            undo[key] = getattr(self, key)
            setattr(self, key, value)
        return self.SetParameters, undo

    def SaveToFile(self, file, *args, **kw):
        if self.class_name:
            apply(file.BeginPluginCompound, (self.class_name,) + args, kw)
            for obj in self.objects:
                obj.SaveToFile(file)
            file.EndPluginCompound()
        else:
            raise SketchError("Plugin %s doesn't define a class name"
                              % self.__class__)

class TrafoPlugin(PluginCompound):

    def __init__(self, trafo = None, duplicate = None, loading = 0):
        PluginCompound.__init__(self, duplicate = duplicate)
        if duplicate is not None:
            self.trafo = duplicate.trafo
        else:
            if trafo is None:
                trafo = Identity
            elif type(trafo) == TupleType:
                trafo = apply(Trafo, trafo)
            elif isinstance(trafo, TrafoType):
                # trafo is already a trafo object
                pass
            else:
                # assume a number and interpret it as a scaling transformation
                trafo = Scale(trafo)
            self.trafo = trafo

    def recompute(self):
        # Implement this in the derived class to update the children.
        pass

    def SetParameters(self, kw):
        undo = PluginCompound.SetParameters(self, kw)
        try:
            self.recompute()
        except:
            Undo(undo)
            raise
        return undo

    def set_transformation(self, trafo):
        undo = (self.set_transformation, self.trafo)
        self.trafo = trafo
        self.recompute()
        return undo

    def Transform(self, trafo):
        undo = [self.begin_change_children()]
        try:
            undo.append(PluginCompound.Transform(self, trafo))
            undo.append(self.set_transformation(trafo(self.trafo)))
            undo.append(self.end_change_children())
        except:
            undo.reverse()
            map(Undo, undo)
            raise
        return CreateListUndo(undo)

    def Translate(self, offset):
        return self.Transform(Translation(offset))

    def RemoveTransformation(self):
        return self.set_transformation(Translation(self.trafo.offset()))

    def LayoutPoint(self):
        return self.trafo.offset()

    def Trafo(self):
        return self.trafo


class UnknownPlugin(PluginCompound):

    is_Group = 1
    changed = 0

    def __init__(self, class_name = '', *args, **kw):
        if kw.has_key('loading'):
            del kw['loading']
        duplicate = kw.get('duplicate')
        if duplicate is not None:
            self.class_name = duplicate.class_name
            self.args = duplicate.args
            self.kw = duplicate.kw
        else:
            self.class_name = class_name
            self.args = args
            self.kw = kw
        PluginCompound.__init__(self, duplicate = duplicate)
        self.disguise()

    def disguise(self):
        # If self has only one child, try to behave like it:
        if len(self.objects) == 1:
            object = self.objects[0]
            if object.is_curve:
                self.is_curve = object.is_curve
                self.AsBezier = object.AsBezier
                self.Paths = object.Paths

    def _changed(self):
        PluginCompound._changed(self)
        self.changed = 1

    def load_Done(self):
        PluginCompound.load_Done(self)
        self.disguise()

    def SaveToFile(self, file):
        if not self.changed:
            apply(PluginCompound.SaveToFile, (self, file) + self.args, self.kw)
        else:
            # XXX an alternative approach for a changed UnknownPlugin
            # might be to store explicitly as an 'UnknownPlugin' so that
            # always is represented by this class.
            if len(self.objects) == 1:
                self.objects[0].SaveToFile(file)
            else:
                # Save as ordinary group. This might not be a good idea,
                # since the UnknownPlugin may (in the future) have some
                # special behaviour, that the group doesn't have.
                file.BeginGroup()
                for obj in self.objects:
                    obj.SaveToFile(file)
                file.EndGroup()


    def Info(self):
        return _("Unknown Plugin Object `%s'") % self.class_name

    Ungroup = PluginCompound.GetObjects


# XXX this implementation of the UnknownPlugin my fail in some
# situations.
#
# The following example is now fixed, but similar situations are
# conceivable:
#
# If the plugin defines is_curve and AsBezier(), for instance, it can be
# used as a control object in a BlendGroup with, say, a PolyBezier
# object as the other control object. The UnknownPlugin does not provide
# that interface, so that loading a document containing such a
# BlendGroup would fail, because the UnknownPlugin instance cannot be
# converted to a PolyBezier object when the interpolation is recomputed.
#
# A solution would be to make the UnknownPlugin behave different if it
# only has one child. Or to have it examine its children and try to
# support the interfaces they have in common. I.e. if all children have
# is_curve == 1, it could also set its instance variable is_curve to 1,
# and implement the AsBezier meghod by returning a PolyBezier object
# that is the combination (`Combine Beziers') of its converted children.
# This would not always be appropriate, however.
#
# Are there other cases where UnknownPlugin has shortcomings?

