# -*- coding: utf-8 -*-
############################################################
# Copyright © 2005-2010  Benoît PIN <benoit.pin@ensmp.fr>  #
# Plinn - http://plinn.org                                 #
#                                                          #
# This program is free software; you can redistribute it   #
# and/or modify it under the terms of the Creative Commons #
# "Attribution-Noncommercial 2.0 Generic"                  #
# http://creativecommons.org/licenses/by-nc/2.0/           #
############################################################
""" Image threaded batch computation module
"""

import threading
import logging
import atexit
from types import StringTypes
from math import ceil
import transaction
from ZODB.POSException import ConflictError
from zope.site.hooks import setSite
from cStringIO import StringIO

console = logging.getLogger('[manipulation thread]')

class ImageQueueProcessorThread(threading.Thread) :
	"""This thread is started at zope startup
	"""

	__stopped = False
	
	
	def __init__(self, portal_path, itemsPath) :
		threading.Thread.__init__(self)
		self.portal_path = portal_path
		self.queue = []
		if isinstance(itemsPath, StringTypes) :
			itemsPath = [itemsPath]
		for i in itemsPath :
			self.queueAdd(i)
	
	@property
	def queueSize(self) :
		return len(self.queue)
	
	def queueAdd(self, itemPath) :
		self.queue.append(itemPath)

	def run(self) :
		console.info('process started.')
		#atexit.register(self.stop)
		import Zope2
		app = Zope2.app()
		portal = app.unrestrictedTraverse(self.portal_path)
		setSite(portal)
		while not self.__stopped and self.queueSize :
			self._process(app)
		
		con = app._p_jar
		con.close()
		#con.close()
		console.info('process finished.')
		#print con
		#print con.transaction_manager
		

	def stop(self):
		console.info('process stopped.')
		self.__stopped = True
	
	def _process(self, app) :
		path = self.queue.pop(0)
		try :
			p = app.unrestrictedTraverse(path)
		except KeyError :
			console.warn('deleted during processing: %s' % path)
			return
		
		console.info('%d : %s' % (self.queueSize, p.absolute_url()))

		try :
			if not hasattr(p, 'thumbnail'):
				p.makeThumbnail()
				# print 'make thumbnail'

			for size in ((500, 500), (600, 600), (800, 800)) :
				# print 'resize at', size
				p._getResizedImage(size, True)
				transaction.commit()
			
			zMin = p.tiles_min_zoom
			zMax = p.tiles_max_zoom
			zStep = p.tiles_step_zoom
			levels = range(zMin, zMax + zStep, zStep)
			zooms = [l/100. for l in levels]
			todo =  set(zooms) - set(p._tiles.keys())
			if todo :
				if p.tileGenerationLock.locked() :
					console.info('skip %s: already tiling.' % p.absolute_url())
					return
				
				p.tileGenerationLock.acquire()
				zooms = list(todo)
				zooms.sort()
				ppm = None
				try :
					ppm = p._getPPM()
					for zoom in zooms :
					
						# print 'tiling at', zoom
						if zoom < 1 :
							rppm = ppm.resize(ratio=zoom)
						else :
							rppm = ppm
						p._makeTilesAt(zoom, rppm)
						del rppm
						transaction.commit()
				finally :
					del ppm
					p.tileGenerationLock.release()
				
			try :
				delattr(p, '_v__methodResultsCache')
			except AttributeError:
				pass
			
			p.tiles_available = 1
			assert p._getCatalogTool()
			p.reindexObject(idxs=['tiles_available'])
			transaction.commit()

		except ConflictError :
			console.warn('Resync after ZODB ConflicError')
			transaction.abort()
			portal = app.unrestrictedTraverse(self.portal_path)
			portal._p_jar.sync()
			self.queueAdd(path)
			return
		except :
			p.tiles_available = -1
			import traceback
			out = StringIO()
			traceback.print_exc(None, out)
			console.error(out.getvalue())			
