#!/usr/bin/python
#
#   imageclassify - simplify manual image classification and comparison
#   Copyright (C) 2003 Matthew Mueller <donut@dakotacom.net>
#
#   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import os,sys,re
import Image, ImageTk, Tkinter

editor = 'gimp-remote'

def quit(*args):
	sys.exit(0)

class ImageClassifier:
	def __init__(self, args):
		self.dirlist = []
		self.filelist = []
		self.filelist2 = {}
		self.comparemode = 0
		for a in args:
			if a == '--':
				if self.comparemode:
					print '-- specified more than once'
					quit()
				self.comparemode = 1
			else:
				if os.path.isdir(a):
					self.dirlist.append(a)
				elif os.path.isfile(a):
					if self.comparemode:
						self.filelist2[os.path.split(a)[1]] = a
					else:
						self.filelist.append(a)

		#dirargs = [d for d in args if os.path.isdir(d)]
		#fileargs = [d for d in args if os.path.isfile(d)]
		#filelist = os.listdir('.')
		if not self.dirlist:
			self.dirlist = [d for d in os.listdir('.') if os.path.isdir(d)]
		if not self.filelist:
			self.filelist = [d for d in os.listdir('.') if os.path.isfile(d)]
		self.dirlist.sort()
		filelistsort = [(os.path.split(f)[1],f) for f in self.filelist]
		filelistsort.sort()
		self.filelist = [f for n,f in filelistsort]
		self.cur = 0
		if not self.filelist:
			print 'no images!'
			quit()
		
		self.root = root = Tkinter.Tk()
		root.bind("q", quit)
		root.bind("e", self.evEdit) #edit left file
		root.bind("E", self.evEdit) #edit right file (in comparemode)
#TODO	root.bind("u", self.evUndoMove)
		root.bind("<Home>", self.evFirst)
		root.bind("<End>", self.evLast)
		root.bind("<Down>", self.evNext)
		root.bind("<Up>", self.evPrev)
		root.bind("<Right>", self.evNext)
		root.bind("<Left>", self.evPrev)
		root.bind("<Return>", self.evNext)
		root.bind("<Prior>", self.evPrevGroup)
		root.bind("<Next>", self.evNextGroup)
	
		self.dirframe = Tkinter.Frame(self.root)
		self.dirframe.pack(side=Tkinter.LEFT)
		for i in range(0,min(len(self.dirlist),9)):
			#print "dir %i: %s"%(i+1,self.dirlist[i])
			root.bind(str(i+1), self.evMove)
			Tkinter.Label(self.dirframe, text="%i: %s"%(i+1,self.dirlist[i])).pack()

		#self.tilesize = 384
		self.tilesize = 512

		if self.comparemode:
			self.canvas = Tkinter.Canvas(root,width=self.tilesize*3+4,height=self.tilesize*2+4)
		else:
			self.canvas = Tkinter.Canvas(root,width=self.tilesize*2+4,height=self.tilesize*2+4)
		self.canvas.pack(expand=1,fill='both')
		self.cbg = [self.canvas.create_rectangle(1,1,1,1,fill='black') for i in range(0,8)]
		self.cimage = None
		self.displayCur()

	def getCompareFile(self):
		filename = os.path.split(self.filelist[self.cur])[1]
		return self.filelist2.get(filename)
	
	def imageOpenZoomed(self, filename):
		image = Image.open(filename)
		osx,osy = image.size
		omaxsize = max(osx,osy)
		zoom = self.tilesize//omaxsize or 1 #TODO: make tilesize based on current window size
		if zoom!=1:
			image = image.resize((osx*zoom, osy*zoom))
		return image,osx,osy,zoom
	
	def imageOpenSized(self, filename, size):
		image = Image.open(filename)
		osx,osy = image.size
		if image.size!=size:
			image = image.resize(size)
		zoomx = image.size[0]*1.0/osx
		zoomy = image.size[1]*1.0/osy
		if zoomx!=zoomy:
			zoom = '%s,%s'%(zoomx,zoomy)
		else:
			zoom = zoomx
		return image,osx,osy,zoom
	
	def displayCur(self):
		if self.cimage:
			map(self.canvas.delete, self.cimage)
		self.image = None
		self.image2 = None
		while not self.image:
			try:
				filename = self.filelist[self.cur]
				self.image,osx,osy,zoom = self.imageOpenZoomed(filename)
			except IOError, e:
				print str(e), self.filelist[self.cur]
				self.filelist.pop(self.cur)
				if not self.filelist:
					print "no images left!"
					quit()
				self.cur %= len(self.filelist)
		self.pimage = ImageTk.PhotoImage(self.image)
		sx,sy = self.image.size
		title='(%i/%i) %s %ix%i (%ix)'%(self.cur+1, len(self.filelist), filename, osx, osy, zoom)
		if self.comparemode:
			filename2 = self.getCompareFile()
			if filename2:
				try:
					self.image2,o2sx,o2sy,zoom2 = self.imageOpenSized(filename2, self.image.size)
					self.pimage2 = ImageTk.PhotoImage(self.image2)
					title += ' -- %s %ix%i (%ix)'%(filename2, o2sx, o2sy, zoom2)
				except IOError, e:
					title += ' -- %s %s'%(str(e), filename2)
			else:
				title += ' -- %s %s'%('not in filelist2:', filename)
		self.root.title(title)
		#upper left square = same size as image
		self.canvas.coords(self.cbg[0], 3, 3, sx+2, sy+2)
		#lower right square = same size as image
		self.canvas.coords(self.cbg[1], sx+3, sy+3, sx*2+2, sy*2+2)
		if self.comparemode:
			self.canvas.coords(self.cbg[6], sx*2+3, 3, sx*3+2, sy+2)
			#upper border = 2 pixels above image
			self.canvas.coords(self.cbg[7], sx+3, 1, sx*2+2, 2)
			#lower right border = 2 pixels below and to right of image
			self.canvas.coords(self.cbg[2], sx*2+3, sy*2+3, sx*3+2, sy*2+4)
			self.canvas.coords(self.cbg[3], sx*3+3, sy+3, sx*3+4, sy*2+2)
		else:
			#upper right border = 2 pixels above and to right of image
			self.canvas.coords(self.cbg[2], sx+3, 1, sx*2+2, 2)
			self.canvas.coords(self.cbg[3], sx*2+3, 1, sx*2+4, sy+2)
		#lower left border = 2 pixels below and to left of image
		self.canvas.coords(self.cbg[4], 1, sy+3, 2, sy*2+2)
		self.canvas.coords(self.cbg[5], 1, sy*2+3, sx+2, sy*2+4)
		#tile image
		if self.comparemode:
			self.cimage = [self.canvas.create_image(3, 3, anchor=Tkinter.NW, image=self.pimage),
				self.canvas.create_image(3+sx, 3, anchor=Tkinter.NW, image=self.pimage),
				self.canvas.create_image(3, 3+sy, anchor=Tkinter.NW, image=self.pimage)]
			if self.image2:
				self.cimage.extend([
					self.canvas.create_image(3+sx, 3+sy, anchor=Tkinter.NW, image=self.pimage2),
					self.canvas.create_image(3+sx*2, 3, anchor=Tkinter.NW, image=self.pimage2),
					self.canvas.create_image(3+sx*2, 3+sy, anchor=Tkinter.NW, image=self.pimage2)])
		else:
			self.cimage = [self.canvas.create_image(3, 3, anchor=Tkinter.NW, image=self.pimage),
				self.canvas.create_image(3+sx, 3, anchor=Tkinter.NW, image=self.pimage),
				self.canvas.create_image(3, 3+sy, anchor=Tkinter.NW, image=self.pimage),
				self.canvas.create_image(3+sx, 3+sy, anchor=Tkinter.NW, image=self.pimage)]
		
	def displayNext(self, i=1):
		self.cur = (self.cur + i) % len(self.filelist)
		self.displayCur()
	
	def displayNextGroup(self, i=1):
		basere = re.compile("^(.*?)[0-9]*(\.[^.]*)?$")
		start = self.cur
		x = basere.match(self.filelist[self.cur])
		if not x:
			self.displayNext(i)
			return
		base = x.group(1)
		while 1:
			self.cur = (self.cur + i) % len(self.filelist)
			if self.cur == start:
				#no other groups were found, just display next
				self.displayNext(i)
				return
			x = basere.match(self.filelist[self.cur])
			if not x or x.group(1)!=base:
				self.displayCur()
				return
	
	def evMove(self, e):
		n = int(e.char)-1
		if n<0 or n>=len(self.dirlist):
			return
		filename = self.filelist[self.cur]
		destname = os.path.join(self.dirlist[n], os.path.split(filename)[1])
		if os.path.exists(destname):
			print destname,'already exists!'
			return
		print 'move',filename,'->',destname
		os.rename(filename, destname)
		self.filelist.pop(self.cur)
		if not self.filelist:
			print "classified all images!"
			quit()
		self.cur %= len(self.filelist)
		self.displayCur()

	def evNextGroup(self, e):
		self.displayNextGroup()

	def evPrevGroup(self, e):
		self.displayNextGroup(-1)
	
	def evNext(self, e):
		self.displayNext()
		
	def evPrev(self, e):
		self.displayNext(-1)

	def evFirst(self, e):
		self.cur = 0
		self.displayCur()

	def evLast(self, e):
		self.cur = len(self.filelist)-1
		self.displayCur()
	
	def evEdit(self, e):
		if e.char=='E':
			filename = self.getCompareFile()
		else:
			filename = self.filelist[self.cur]
		if filename:
			print 'running',editor,filename
			os.spawnlp(os.P_NOWAIT, editor, editor, filename)
			


def main():
	classifier = ImageClassifier(sys.argv[1:])
	classifier.root.mainloop()

if __name__=="__main__":
	main()
