#=======================================================================

__version__ = '''0.0.01'''
__sub_version__ = '''20130521225013'''
__copyright__ = '''(c) Alex A. Naanou 2012'''


#-----------------------------------------------------------------------

import os
import Image
import json
import sha
import urllib2

from pli.logictypes import OR

import gid


#-----------------------------------------------------------------------
# XXX fanatically cleanup and normalise paths...
# XXX use real uuid's...
#
# TODO:
# 	- load config from file...
# 	- accept a path on command-line
# 	- default path is cwd
# 	- support nested fav's for ribbons
#
# Long Term TODO:
# 	- support processed images
#
#
#-----------------------------------------------------------------------

config = {
	'format-version': '2.0',

	'cache-structure': {
		# XXX make these as close to standard as possible and keep
		# 	  sane distances...
		'150px': '.ImageGridCache/150px/',
		'350px': '.ImageGridCache/350px/',
		'900px': '.ImageGridCache/900px/',
		'1080px': '.ImageGridCache/1080px/',
		'1920px': '.ImageGridCache/1920px/',
	},

	# gen1 format...
	'json': '.ImageGridCache/all.json',

	# gen3 format...
	'images': '.ImageGridCache/images.json',
	'data': '.ImageGridCache/data.json',
	'marked': '.ImageGridCache/marked.json',

	'error': '.ImageGridCache/error.log',
	'sizes': {
		'150px': 150,
		'350px': 350,
		'900px': 900,
		'1080px': 1080,
		'1920px': 1920,
	}
}


images = {
	'position': 0,
	'ribbons':[
		{}
	]
}


IMAGE_EXT = OR(*(
		'.jpg', '.jpeg', '.JPG', '.JPEG',
))

ERR_LOG = '''\
ERROR: %(error)s
SOURCE: %(source-file)s
TARGET: %(target-file)s


'''

CACHE_FILE_NAME = '%(guid)s - %(name)s'


#-----------------------------------------------------------------------

def pathjoin(*p):
	'''
	'''
	return ('/'.join(p)).replace('//', '/')

def log_err(path, e, source_file, target_file):
	'''
	'''
	err_file = pathjoin(path, config['error'])
	if not os.path.exists(err_file):
		err = open(err_file, 'w')
	else:
		err = open(err_file, 'a')
	with err:
		err.write(ERR_LOG % {
				'source-file': source_file,
				'target-file': target_file,
				'error': e,
		})

# this should:
# 	1) see if image is cached, if yes return the cached guid (if dates match)...
# 	2) read the image file and get its guid 
##!!!
def get_image_guid(path, force=False):
	'''
	'''
	im = Image.open(path)
	return sha.sha(im.tostring()).hexdigest()
##	return sha.sha(open(path, 'r').read())


# return list of paths ending in a pattern...
def build_cache_dirs(path, config=config):
	'''
	'''
	dirs = config['cache-structure']
	for _, k in dirs.items():
		p = pathjoin(path, k)
		if not os.path.exists(p):
			os.makedirs(p)


# image format:
# 	{
# 		'id': <image-id>,
# 		'preview': {
# 			<resolution>: <cache-path>,
# 			...
# 		},
# 		'path': <image-path>
# 	}
#
def build_index(path, images=None, count=None):
	'''
	'''
	dirs = config['cache-structure']
	sizes = config['sizes'] 
	n = -1
	if images == None:
		images = {
			'position': 0,
			'ribbons': [{}],
		}

	for name in os.listdir(path):
		# skip non-images...
		iid, ext = os.path.splitext(name)
		if ext != IMAGE_EXT:
			continue

		##!!! this is here for debuging...
		n += 1
		if count != None and n >= count:
			break

		i =  {
			'id': iid,
			'preview': {},
			##!!! absolute paths???
			'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
		}
		img = Image.open(pathjoin(path, name), 'r')
		try:
			iid = sha.sha(img.tostring()).hexdigest()
		except IOError, e:
			print 'x',
			log_err(path, e, name, '-')
			continue

		if iid in images['ribbons'][0]:
			print '_',
			continue
		i['id'] = iid
		images['ribbons'][0][iid] = i
		print '.',

	return images


# XXX this will not overwrite existing files...
# XXX make this destingwish absolute and relative paths...
def make_cache_images(path, images, config=config):
	'''
	'''
	dirs = config['cache-structure']
	sizes = config['sizes'] 
	n = 0

	for name in os.listdir(path):
		# skip non-images...
		iid, ext = os.path.splitext(name)
		source_path = pathjoin(path, name)
		if ext != IMAGE_EXT:
			continue
		n += 1
		i =  {
			'id': iid,
			'preview': {},
##			'path': pathjoin(path, name),
			##!!! absolute paths???
			'path': 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:'),
			'ctime': os.path.getctime(source_path),
		}
		img = Image.open(source_path, 'r')
		try:
			##!!! use a real gid -- gid.image_gid(path, ...)
			iid = sha.sha(img.tostring()).hexdigest()
		except IOError, e:
			print 'x',
			log_err(path, e, name, '-')
			i['error'] = 'IOError: ' + str(e)
			continue
		finally:
			# we need to know which images are dead...
			images['ribbons'][0][iid] = i
		i['id'] = iid
		# add original image to struct...
##		i['preview'][str(max(*img.size)) + 'px'] = pathjoin(path, name)
		i['preview'][str(max(*img.size)) + 'px'] = 'file:///' + urllib2.quote(pathjoin(path, name), safe='/:')
		# previews...
		for k, spec in sizes.items():
			p = pathjoin(path, dirs[k], CACHE_FILE_NAME % {'guid': iid, 'name': name})
			# do not upscale images...
			if max(*img.size) <= spec:
				continue
			# add image to index...
			if not os.path.exists(p):
				scale = spec/float(max(*img.size))
				preview = img.resize((int(img.size[0]*scale), int(img.size[1]*scale)), Image.ANTIALIAS)
				preview.save(p)
				##!!! metadata???
				##!!!
				print '.',
			else:
				# indicate an image skip...
				print '_',
##			i['preview'][str(spec) + 'px'] = p
			##!!! absolute paths???
			i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
		# put original image in cache...
##		i['preview'][str(spec) + 'px'] = p
		i['preview'][str(spec) + 'px'] = 'file:///' + urllib2.quote(p, safe='/:')
	images['position'] = images['ribbons'][0].keys()[0]
	with open(pathjoin(path, config['json']), 'w') as f:
		json.dump(images, f, indent=4)
	##!!! STUB...
	return n



#-----------------------------------------------------------------------

def build_local_cache(path):
	'''
	'''
	import time

	t0 = time.time()

	build_cache_dirs(path)

	n = make_cache_images(path, images)

	t1 = time.time()

	print
	print 'Processed %s images in %s seconds.' % (n, t1-t0)




#-----------------------------------------------------------------------
if __name__ == '__main__':
	from optparse import OptionParser

	parser = OptionParser()

	##!!! need to define the path so that it shoes up in -h

	options, args = parser.parse_args()

	if len(args) != 1:
		parser.print_usage()
	else:
		IN_PATH = args[0]
		IN_PATH = IN_PATH.replace('\\', '/')
		build_local_cache(IN_PATH)


##	PATH = 'images/cache-test/'
##	PATH = 'L:/incoming/UNSORTED/Images/fav'

##	build_local_cache(PATH)

##	index = build_index(PATH, count=10)
##
##	import IPython
##	IPython.embed()




#=======================================================================
#                                            vim:set ts=4 sw=4 nowrap :
