# -*- coding: utf-8 -*-
#######################################################################################
#   Photo is a part of Plinn - http://plinn.org                                       #
#   Copyright (C) 2004-2007  Benoît PIN <benoit.pin@ensmp.fr>                         #
#                                                                                     #
#   This program is free software; you can redistribute it and/or                     #
#   modify it under the terms of the GNU General Public License                       #
#   as published by the Free Software Foundation; either version 2                    #
#   of the License, or (at your option) any later version.                            #
#                                                                                     #
#   This program 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 General Public License for more details.                                      #
#                                                                                     #
#   You should have received a copy of the GNU General Public License                 #
#   along with this program; if not, write to the Free Software                       #
#   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.   #
#######################################################################################
""" Tile support module



"""

from AccessControl import ClassSecurityInfo
from AccessControl import Unauthorized
from AccessControl import getSecurityManager
from AccessControl.Permissions import view, change_images_and_files
from PIL import Image as PILImage
from math import ceil
from blobbases import Image
from xmputils import TIFF_ORIENTATIONS
from cache import memoizedmethod
from BTrees.OOBTree import OOBTree
from BTrees.IOBTree import IOBTree
from ppm import PPMFile
from threading import Lock
from subprocess import Popen, PIPE
from tempfile import TemporaryFile

JPEG_ROTATE = 'jpegtran -rotate %d'
JPEG_FLIP = 'jpegtran -flip horizontal'

def runOnce(lock):
	""" Decorator. exit if already running """

	def wrapper(f):
		def method(*args, **kw):
			if not lock.locked() :
				lock.acquire()
				try:
					return f(*args, **kw)
				finally:
					lock.release()
			else :
				return False
		return method
	return wrapper



class TileSupport :
	""" Mixin class to generate tiles from image """
	
	security = ClassSecurityInfo()
	tileSize = 256
	tileGenerationLock = Lock()
	
	def __init__(self) :
		self._tiles = OOBTree()
	
	security.declarePrivate('makeTilesAt')
	@runOnce(tileGenerationLock)
	def makeTilesAt(self, zoom):
		"""generates tiles at zoom level"""
		
		if self._tiles.has_key(zoom) :
			return True
		
		assert zoom <= 1, "zoom arg must be <= 1 found: %s" % zoom

		ppm = self._getPPM()
		if zoom < 1 :
			ppm = ppm.resize(ratio=zoom)
		
		self._makeTilesAt(zoom, ppm)
		return True
	
	def _getPPM(self) :
		bf = self._getJpegBlob()
		f = bf.open('r')
		
		orientation = self.tiffOrientation()
		rotation, flip = TIFF_ORIENTATIONS.get(orientation, (0, False))
		
		if rotation and flip :
			tf = TemporaryFile(mode='w+')
			pRot = Popen(JPEG_ROTATE % rotation
					, stdin=f
					, stdout=PIPE
					, shell=True)
			pFlip = Popen(JPEG_FLIP
						, stdin=pRot.stdout
						, stdout=tf
						, shell=True)
			pFlip.wait()
			f.close()
			tf.seek(0)
			f = tf

		elif rotation :
			tf = TemporaryFile(mode='w+')
			pRot = Popen(JPEG_ROTATE % rotation
						, stdin=f
						, stdout=tf
						, shell=True)
			pRot.wait()
			f.close()
			tf.seek(0)
			f = tf

		elif flip :
			tf = TemporaryFile(mode='w+')
			pFlip = Popen(JPEG_FLIP
						, stdin=f
						, stdout=tf
						, shell=True)
			pFlip.wait()
			f.close()
			tf.seek(0)
			f = tf

		ppm = PPMFile(f, tileSize=self.tileSize)
		f.close()
		return ppm
	
	def _makeTilesAt(self, zoom, ppm):
		hooks = self._getAfterTilingHooks()		
		self._tiles[zoom] = IOBTree()
		bgColor = getattr(self, 'tiles_background_color', '#fff')
		
		for x in xrange(ppm.tilesX) :
			self._tiles[zoom][x] = IOBTree()
			for y in xrange(ppm.tilesY) :
				tile = ppm.getTile(x, y)
				for hook in hooks :
					hook(self, tile)
				
				# fill with solid color
				if min(tile.size) < self.tileSize :
					blankTile = PILImage.new('RGB', (self.tileSize, self.tileSize), bgColor)
					box = (0,0) + tile.size
					blankTile.paste(tile, box)
					tile = blankTile
				
				zImg = Image('tile', 'tile', '', content_type='image/jpeg')
				out = zImg.open('w')
				tile.save(out, 'JPEG', quality=90)
				zImg.updateFormat(out.tell(), tile.size, 'image/jpeg')
				out.close()

				self._tiles[zoom][x][y] = zImg
		
	def _getAfterTilingHooks(self) :
		return []
	
	
	security.declareProtected(view, 'getAvailableZooms')
	def getAvailableZooms(self):
		zooms = list(self._tiles.keys())
		zooms.sort()
		return zooms
	
	security.declareProtected(view, 'getTile')
	def getTile(self, REQUEST, RESPONSE, zoom=1, x=0, y=0):
		""" publishes tile
		"""
		zoom, x, y = float(zoom), int(x), int(y)
		if not self._tiles.has_key(zoom) :
			sm = getSecurityManager()
			if not sm.checkPermission(change_images_and_files, self) :
				raise Unauthorized("Tiling arbitrary zoom unauthorized")
			if self.makeTilesAt(zoom) :
				tile = self._tiles[zoom][x][y]
				return tile.index_html(REQUEST=REQUEST, RESPONSE=RESPONSE)
		else :
			tile = self._tiles[zoom][x][y]
			return tile.index_html(REQUEST=REQUEST, RESPONSE=RESPONSE)

