# -*- coding: utf-8 -*-
####################################################
# Copyright © 2009 Luxia SAS. All rights reserved. #
#                                                  #
# Contributors:                                    #
#  - Benoît Pin <pinbe@luxia.fr>                   #
####################################################
""" PPM File support module



"""

from subprocess import Popen, PIPE
from tempfile import TemporaryFile
import os
from math import ceil
from PIL.Image import open as imgopen
from PIL.Image import fromstring
from PIL.Image import ANTIALIAS
from cStringIO import StringIO

DGJPEG = 'djpeg'
RESIZING_TILE_SIZE = 1024

class PPMFile(object) :
	
	def __init__(self, f, tileSize=256, isRaw=False) :
		# convert jpeg -> ppm with djpeg
		if not isRaw :
			# print 'djpeg'
			self.fp = TemporaryFile(mode='w+')
			p = Popen(DGJPEG, stdin=f, stdout=self.fp, stderr=PIPE, shell=True)
			p.wait()
			err = p.stderr.read()
			if err :
				raise SystemError, err
		else :
			self.fp = f
		
		# get image specs with PIL
		self.fp.seek(0)
		im = imgopen(self.fp)
		decoder, region, offset, parameters = im.tile[0]
		x, y, width, height = region
		del im
		assert decoder == 'raw'
		mode = parameters[0]
		assert mode in ('RGB', 'L'), "Unsupported mode %s" % mode

		if mode == 'RGB' :
			sampleSize = 3
		elif mode == 'L' :
			sampleSize = 1
		
		self.width = width
		self.height = height
		self.offset = offset
		self.mode = parameters[0]
		self.sampleSize = sampleSize
		self._setTileSize(tileSize)
	
	def _setTileSize(self, tileSize) :
		self.tileSize = tileSize
		self.tilesX = int(ceil(float(self.width) / self.tileSize))
		self.tilesY = int(ceil(float(self.height) / self.tileSize))
	
	def getTile(self, xt, yt) :
		f = self.fp
		ss = self.sampleSize
		x = xt * self.tileSize
		y = yt * self.tileSize
		start = (self.width * y + x) * ss + self.offset
		
		tw = th = self.tileSize

		bw = self.width - x
		if bw < self.tileSize  :
			tw = bw
		bh = self.height - y
		if bh < self.tileSize :
			th = bh
		
		assert tw > 0 and th > 0, "Tile requested out of image."

		size = (tw, th)
		tll = tw * ss
		jump = (self.width - tw) * ss
		
		f.seek(start)
		data = StringIO()
		
		for line in xrange(size[1]) :
			data.write(f.read(tll))
			f.seek(jump, 1)
		
		data.seek(0)
		im = fromstring(self.mode, size, data.read())
		return im
	
	def getTileSequence(self):
		seq = []
		for y in xrange(self.tilesY) :
			for x in xrange(self.tilesX) :
				seq.append((x, y))
		return seq
	
	def resize(self, ratio=None, maxLength=None) :
		if ratio and maxLength :
			raise AttributeError("'ratio' and 'size' are mutually exclusive.")
		if maxLength :
			maxFullLength = max(self.width, self.height)
			ratio = float(maxLength) / maxFullLength

		tileSizeBak = self.tileSize
		
		self._setTileSize(RESIZING_TILE_SIZE)
		
		width = height = 0
		# cumul des arrondis
		width = int(round(self.tileSize * ratio)) * (self.tilesX -1)
		width += int(round((self.width - self.tileSize * (self.tilesX -1)) * ratio))
		
		height = int(round(self.tileSize * ratio)) * (self.tilesY -1)
		height += int(round((self.height - self.tileSize * (self.tilesY -1)) * ratio))
		
		magic = self.mode == 'RGB' and 6 or 5
		head = 'P%d %d %d 255\n' % (magic, width, height)
		offset = len(head)

		out = TemporaryFile(mode='w+')
		out.write(head)

		ss = self.sampleSize
		rTll = int(round(self.tileSize * ratio))
		
		for x, y in self.getTileSequence() :
			# print 'resize', (x,y)
			tile = self.getTile(x,y)
			tileSize = tile.size
			size = map(lambda l : int(round(l * ratio)), tileSize)
			
			if size[0] and size[1] :
				resized = tile.resize(size, ANTIALIAS)
				data = resized.tostring()
				
				start = (y * width + x) * ss * rTll + offset
				jump = (width - size[0]) * ss
				
				out.seek(start)
				tll = size[0] * ss
				
				# écriture dans le bon ordre (c'est quand même plus agréable à l'œil)
				for l in xrange(size[1]) :
					lineData = data[l*tll:(l+1)*tll]
					out.write(lineData)
					out.seek(jump, 1)
		
		out.seek(0,2)
		length = out.tell()
		assert length - len(head) == width * height * ss, (length - len(head), width * height * ss)
		out.seek(0)

		self._setTileSize(tileSizeBak)
		return PPMFile(out, tileSize=tileSizeBak, isRaw=True)
	
	def getImage(self) :
		self.fp.seek(0)
		return imgopen(self.fp)
	
	def __del__(self) :
		self.fp.close()


if __name__ == '__main__' :
	f = open('/Users/pinbe/Desktop/Chauve_souris.jpg')
	try :
		ppm = PPMFile(f, tileSize=256)
		rppm = ppm.resize(maxLength=800)
		im = rppm.getImage()
		im.show()
		for x, y in ppm.getTileSequence() :
			im = ppm.getTile(x, y)
			im.save('testoutput/%d_%d.jpg' % (x, y), 'JPEG', quality=90)
	finally :
		f.close()
