# -*- coding: utf-8 -*-
#######################################################################################
#   Plinn - http://plinn.org                                                          #
#   Copyright © 2009  Benoît Pin <pin@cri.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.   #
#######################################################################################
""" 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()
