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

# A simple graphics object that represents a pixel image. It uses the
# Python Image Library for file I/O. The ExternalData baseclass
# maintains a cache of images to avoid having multiple copies of data in
# memory.
#

import os
from types import StringType

import PIL.Image, PIL.ImageChops

from Sketch import _, RegisterCommands
from Sketch.UI.command import AddCmd

from external import ExternalData, get_cached, ExternalGraphics

class ImageData(ExternalData):

    attributes = {'mode':0, 'size':0, 'im':0, 'info':0}

    def __init__(self, image, filename = '', cache = 1):
	# convert image to mode 'L' or 'RGB' if necessary
	if image.mode not in ('RGB', 'RGBA', 'L'):
	    if image.mode == '1':
		mode = 'L'
	    else:
		mode = 'RGB'
	    image = image.convert(mode)
	else:
	    image.load()
	self.image = image
	ExternalData.__init__(self, filename, cache)

    def __getattr__(self, attr):
	if self.attributes.has_key(attr):
	    return getattr(self.image, attr)
	raise AttributeError, attr

    def AsEmbedded(self):
	if self.filename:
	    return ImageData(self.image)
	else:
	    return self

    def IsEmbedded(self):
	return not self.filename

    def Size(self):
	return self.size

    def Image(self):
        return self.image

    def Convert(self, mode):
	if mode != self.image.mode:
	    return ImageData(self.image.convert(mode))
	else:
	    return self

    def Invert(self):
        return ImageData(PIL.ImageChops.invert(self.image))



def load_image(filename, cache = 1):
    if type(filename) == StringType:
	image = get_cached(filename)
	if image:
	    return image
    image = PIL.Image.open(filename)
    if type(filename) != StringType:
	filename = ''
    return ImageData(image, filename = filename, cache = cache)

class Image(ExternalGraphics):

    is_Image = 1
    is_clip = 1

    commands = ExternalGraphics.commands[:]

    def __init__(self, image = None, imagefile = '', trafo = None,
		 duplicate = None):
	if duplicate is None:
	    if not image:
		if not imagefile:
		    raise ValueError, 'Image must be instantiated with'\
				      ' either image or imagefile'
		image = load_image(imagefile)
	ExternalGraphics.__init__(self, image, trafo,
				  duplicate = duplicate)

    def DrawShape(self, device, rect = None, clip = 0):
	device.DrawImage(self.data, self.trafo, clip)

    def Info(self):
	width, height = self.data.Size()
	x, y = self.trafo.offset()
	if self.IsEmbedded():
	    return _("Embedded Image %(width)d x %(height)d "
		     "at (%(x)d, %(y)d)") % locals()
	else:
	    filename = os.path.basename(self.data.Filename())
	    return _("Linked Image `%(filename)s' %(width)d x %(height)d "
		     "at (%(x)d, %(y)d)") % locals()

    def SaveToFile(self, file):
	file.Image(self.data, self.trafo)

    def IsEmbedded(self):
	return self.data.IsEmbedded()

    def CanEmbed(self):
	return not self.IsEmbedded()

    def Embed(self):
	return self.SetData(self.data.AsEmbedded())
    AddCmd(commands, 'EmbedImage', _("Embed Image"), Embed,
           sensitive_cb = 'CanEmbed')

    def CallImageFunction(self, function, args = ()):
	if type(args) != type(()):
	    args = (args,)
	data = apply(getattr(self.data, function), args)
	return self.SetData(data)
    AddCmd(commands, 'GrayscaleImage', _("Grayscale Image"),
	   CallImageFunction, args = ('Convert', 'L'))
    AddCmd(commands, 'InvertImage', _("Invert Image"), CallImageFunction,
	   args = 'Invert')

    context_commands = ('EmbedImage', 'GrayscaleImage', 'InvertImage')

RegisterCommands(Image)

