/**********************************************************************
* 
*
*
**********************************************************************/

//var DEBUG = DEBUG != null ? DEBUG : true

// XXX move this to the config...
var PREVIEW_SIZES = [
	// NOTE: this is first so as to prevent the hi-res from loading...
	// XXX this is best to be screen sized or just a little bigger...
	1280,
	150,
	350,
	900
]


/*********************************************************************/

// load the target-specific handlers...
// CEF
if(window.CEF_dumpJSON != null){

	console.log('CEF mode: loading...')

	var dumpJSON = CEF_dumpJSON
	var listDir = CEF_listDir
	var removeFile = CEF_removeFile
	var runSystem = CEF_runSystem

// node-webkit
} else if(window.require != null){

	console.log('node-webkit mode: loading...')

	var path = require('path')
	var fs = require('fs')
	var fse = require('fs.extra')
	var proc = require('child_process')
	var node_crypto = require('crypto')

	//var exif = require('exif2')

	var gui = require('nw.gui')


	window.osPath = function(p){
		return path
			// we can have two types of path:
			// 	file:///some/path			-> /some/path
			// 	file:///X:/some/other/path	-> X:/some/other/path
			.normalize(p.replace(/file:\/+([a-zA-Z]:\/|\/)/, '$1'))
	}
	window.execPathPush = function(p){
		process.env.PATH += ';' + path.normalize(path.dirname(process.execPath) + '/' + p)
	}


	// paths to included utils...
	execPathPush('./vips/bin')

	// Things ImageGrid needs...
	// XXX do we need assync versions??
	window.listDir = function(path){
		if(!fs.existsSync(osPath(path))){
			return null
		}
		return fs.readdirSync(osPath(path))
	}
	// XXX make this work across fs...
	// XXX this will not overwrite...
	// XXX set ctime to the same value as the original...
	window.copyFile = function(src, dst){
		var deferred = $.Deferred()
		src = osPath(src)
		dst = osPath(dst)

		var path = dst.split(/[\\\/]/)
		path.pop()
		path = path.join('/')

		// make dirs...
		if(!fs.existsSync(path)){
			console.log('making:', path)
			fse.mkdirRecursiveSync(path)
		}

		if(!fs.existsSync(dst)){
			// NOTE: this is not sync...
			fse.copy(src, dst, function(err){
				if(err){
					deferred.reject(err)
				} else {
					deferred.resolve()
				}
			})
			return deferred
		}
		deferred.notify(dst, 'exists')
		return deferred.resolve()
	}
	window.dumpJSON = function(path, data){
		path = osPath(path)
		var dirs = path.split(/[\\\/]/)
		dirs.pop()
		dirs = dirs.join('/')
		// build path...
		if(!fs.existsSync(dirs)){
			console.log('making:', path)
			fse.mkdirRecursiveSync(path)
		}
		return fs.writeFileSync(path, JSON.stringify(data), encoding='utf8')
	}
	window.removeFile = function(path){
		return fs.unlinkSync(osPath(path))
	}
	window.runSystem = function(path){
		return proc.exec('"'+osPath(path)+'"', function(error, stdout, stderr){
			if(error != null){
				console.error(stderr)
			}
		})
	}

	// XXX this uses vips...
	window.getVipsField = function(field, source){
		if(source in IMAGES){
			var img = IMAGES[source]
			var source = normalizePath(img.path)
		}
		var getter = $.Deferred()

		var data = ''
		var p = proc.spawn('vips', ['im_header_string', field, osPath(source)])
		p.stdout.on('data', function(d){
				data += d.toString()
			})
		p.stdout.on('end', function(){
				getter.resolve(data.trim())
			})

		/* XXX do we need these???
		p.on('error', function(code){
				// XXX
			})
		p.on('close', function(code){
				getter.resolve(data)
			})
		*/

		return getter
	}

	// NOTE: source can be either gid or a path...
	window.getImageOrientation = function(source){
		var getter = $.Deferred()
		getVipsField('exif-ifd0-Orientation', source)
			.done(function(o){
				getter.resolve(orientationExif2ImageGrid(parseInt(o)))
			})
		return getter
	}

	// NOTE: source can be either gid or a path...
	// XXX handle errors...
	window._getImageSize = function(dimension, source){
		if(source in IMAGES){
			var img = IMAGES[source]
			var source = normalizePath(img.path)
		}
		var getter = $.Deferred()

		// get max/min dimension...
		if(dimension == 'max' || dimension == 'min'){
			$.when(
					_getImageSize('width', source), 
					_getImageSize('height', source))
				.done(function(w, h){
					getter.resolve(Math[dimension](w, h))
				})

		// get dimension...
		} else if(dimension == 'width' || dimension == 'height') {
			getVipsField(dimension, source)
				.done(function(res){
					getter.resolve(parseInt(res))
				})

		// wrong dimension...
		} else {
			return getter.reject('unknown dimension:' + dimension)
		}

		return getter
	}

	// XXX API to add to $PATH...

	// preview generation...
	//
	// possible modes:
	// 		- optimized
	// 			use closest rscale and minimal factor
	// 			previews might get artifacts associated with small scale factors
	// 			0.5x time
	// 		- best
	// 			only use scale factor (rscale=1)
	// 			1x time (fixed set of previews: 1280, 150, 350, 900)
	// 		- fast_r 
	// 			make previews using nearest rscale (factor is rounded)
	// 			will produce inexact preview sizes
	// 			0.4x time
	// 		- fast_f
	// 			same as fast_r but factor is floored rather than rounded
	// 			will priduce previews the same size or larger than requested
	// 		- rscale
	// 			only use rscale (factor=1)
	// 			produces only fixed size previews
	// 			0.3x time
	//
	// NOTE: rscale should be used for exactly tuned preview sizes...
	// NOTE: this will add already existing previews to IMAGES[gid]...
	//
	// XXX make this not just vips-specific...
	// XXX path handling is a mess...
	// XXX looks a bit too complex for what it is -- revise!
	window.makeImagePreviews = function(gid, sizes, mode, no_update_loaded){
		mode = mode == null ? 'fast_f' : mode

		var cache_dir = CONFIG.cache_dir

		var img = IMAGES[gid]
		var source = normalizePath(img.path)
		var name = gid +' - '+ source.split(/[\\\/]/).pop()
		var compression = 90

		var previews = []

		// prepare the sizes we are going to be working with...
		if(sizes == null){
			sizes = PREVIEW_SIZES
		} else if(typeof(sizes) == typeof(123)){
			sizes = [ sizes ]
		}

		// build usable local path (without 'file:///')...
		var cache_path = normalizePath(cache_dir)
		cache_path = osPath(cache_path)

		// get cur image size...
		var size_getter = _getImageSize('max', source)

		for(var i=0; i < sizes.length; i++){
			var size = sizes[i]
			// XXX get this from config...
			var target_path = [ cache_path, size+'px' ].join('/')

			var deferred = $.Deferred()
			previews.push(deferred)

			[function(size, target_path, deferred){
				// wait for current image size if needed...
				size_getter.done(function(source_size){

					// handle existing previews...
					if(fs.existsSync(target_path +'/'+ name)){
						// see if we know about the preview...
						if(img.preview == null || !((size+'px') in img.preview)){
							var preview_path = [target_path, name].join('/')
							// add the preview to the image object...
							img.preview[size+'px'] = './' + cache_dir +'/'+ preview_path.split(cache_dir).pop()
							// mark image dirty...
							imageUpdated(gid)
						}
						//console.log('>>> Preview:', name, '('+size+'): Exists.')
						deferred.notify(gid, size, 'exists')
						return deferred.resolve()

					// skip previews larger than cur image...
					} else if(source_size <= size){
						//console.log('>>> Preview:', name, '('+size+'): Skipped.')
						deferred.notify(gid, size, 'skipped')
						return deferred.resolve()
					}

					// create the directory then go to its content...
					// XXX check for errors...
					fse.mkdirRecursive(target_path, function(err){

						var preview_path = [target_path, name].join('/')
						var factor = source_size / size
						// this can be 1, 2, 4 or 8...
						var rscale = 1

						// speed things up with read-scaling and rounding the scale factor...
						if(['fast_r', 'fast_f', 'optimized', 'rscale'].indexOf(mode) >= 0){
							while(rscale < 8){
								if(rscale*2 >= factor){
									break
								}
								rscale *= 2
							}
							factor = factor / rscale
						}
						// factor processing...
						if(mode == 'fast_r'){
							factor = Math.max(Math.round(factor), 1)

						} else if(mode == 'fast_f'){
							// NOTE: .floor(...) will make the images larger than
							// 		the requested size, this will avaoid scale-up
							// 		artifacts...
							factor = Math.max(Math.floor(factor), 1)

						} else if(mode == 'rscale'){
							factor = 1
						}

						var p = proc.spawn('vips', [
								'im_shrink',
								osPath(source) +':'+ rscale,
								preview_path +':'+ compression,
								factor,
								factor
							])
						// XXX is this the correct wat to deal with errors???
						var error = ''
						p.stderr.on('data', function(data){
							error += data.toString()
						})
						//p.stderr.on('end', function(data){
						//})
						p.on('close', function(code){
							// error...
							if(code != 0){
								deferred.notify(gid, size, 'error', error)
								deferred.reject()

							// ok...
							} else {
								// NOTE: the size of the real preview 
								// 		generated might different from 
								// 		the target size...
								deferred.notify(gid, size, 'done')
								// update the image structure...
								if(!('preview' in img)){
									img.preview = {}
								}
								img.preview[size+'px'] = './' + cache_dir +'/'+ preview_path.split(cache_dir).pop()
								// mark image dirty...
								imageUpdated(gid)
								// we are done...
								deferred.resolve()
							}
						})
					})
				})
			}(size, target_path, deferred)]
		}

		var res = $.when.apply(null, previews)

		// update loaded images...
		if(!no_update_loaded){
			res.done(function(){
				var o = getImage(gid)
				if(o.length > 0){
					updateImage(o)
				}
			})
		}

		return res
	}

	// XXX needs more testing...
	// 		- for some reason this is a bit slower than the queued version
	// 			...in spite of being managed by node.js
	// 		- will this be faster on SMP/multi-core?
	window.makeImagesPreviews = function(gids, sizes, mode){
		gids = gids == null ? getClosestGIDs() : gids
		return $.when.apply(null, gids.map(function(gid){
			return makeImagePreviews(gid, sizes, mode)
		}))
	}

	// Queued version of makeImagesPreviews(...)
	window.makeImagesPreviewsQ = function(gids, sizes, mode){
		gids = gids == null ? getClosestGIDs() : gids

		var queue = getWorkerQueue('Generate previews', 4)
			.filling()

		// attach the workers to the queue...
		$.each(gids, function(_, gid){
			queue.enqueue(makeImagePreviews, gid, sizes, mode)
				// XXX do we need to report seporate previews???
				//.progress(function(state){ queue.notify(state) })
				.always(function(){ console.log(gid, 'done') })
		})

		return queue.doneFilling()
	}

	// format: "20130102-122315"
	window.getEXIFDate = function(source){
		var getter = $.Deferred()
		getVipsField('exif-ifd0-Date and Time', source)
			.done(function(date){
				getter.resolve(date
					// remove substrings in braces...
					.replace(/\([^)]*\)/, '')
					.trim()
					.replace(/:/g, '')
					.replace(/ /g, '-'))
			})
		return getter
	}

	window.getEXIFGID = function(source, make_text_gid){
		if(source in IMAGES){
			var img = IMAGES[source]
			var source = normalizePath(img.path)
		}
		var getter = $.Deferred()

		$.when(
				getVipsField('exif-ifd0-Artist', source),
				getEXIFDate(source))
			.done(function(artist, date){
				// Artist...
				artist = artist
					// remove substrings in braces...
					.replace(/\([^)]*\)/, '')
					.trim()
				artist = artist == '' ? 'Unknown' : artist

				// Date...
				// XXX if not set, get ctime...
				// XXX

				// File name...
				var name = source.split(/[\\\/]/).pop().split('.')[0]

				var text_gid = artist +'-'+ date +'-'+ name

				// text gid...
				if(make_text_gid){
					getter.resolve(text_gid)

				// hex gid...
				} else {
					var h = node_crypto.createHash('sha1')
					h.update(text_gid)
					var hex_gid = h.digest('hex')

					getter.resolve(hex_gid)
				}
			})
			// XXX handle arrors in a more informative way...
			.fail(function(){
				getter.reject()
			})
		return getter
	}

	// UI-specific...
	window.toggleFullscreenMode = createCSSClassToggler(
			document.body, 
			'.full-screen-mode',
			function(action){
				gui.Window.get().toggleFullscreen()
			})
	window.closeWindow = function(){
		gui.Window.get().close()
	}
	window.showDevTools = function(){
		gui.Window.get().showDevTools()
	}
	window.reload = function(){
		gui.Window.get().reload()
	}
	window.setWindowTitle = function(text){
		var title = text +' - '+ CONFIG.app_name
		gui.Window.get().title = title
		$('.title-bar .title').text(title)
	}

	// load UI stuff...
	$(function(){
		$('<div class="title-bar"/>')
			.append($('<div class="title"></div>')
				.text($('title').text()))
			.append($('<div class="button close" onclick="closeWindow()">&times;</div>'))
			.appendTo($('body'))
	})



// PhoneGap
} else if(false){

	console.log('PhoneGap mode: loading...')
	// XXX

	// stubs...
	window.toggleFullscreenMode = function(){}
	window.closeWindow = function(){}
	window.showDevTools = function(){}
	window.reload = function(){}



// Bare Chrome...
} else {
	console.log('Chrome mode: loading...')

	// stubs...
	window.toggleFullscreenMode = function(){}
	window.closeWindow = function(){}
	window.showDevTools = function(){}
	window.reload = function(){}
}



/**********************************************************************
* vim:set ts=4 sw=4 :                                                */
