/**********************************************************************
* 
* Ribbon Crop API
*
*
**********************************************************************/

var CROP_STACK = []

var CROP_MODES = []


/******************************************************* Crop Data ***/

function isViewCropped(){
	return CROP_STACK.length != 0
}


function getAllData(){
	if(!isViewCropped()){
		return DATA
	} else {
		return CROP_STACK[0]
	}
}


// NOTE: this will not update .current state...
// NOTE: when keep_ribbons is set, this may generate empty ribbons...
// NOTE: this requieres all data to be present, if currently viewing 
// 		server-side data, then cropping is a server-side operation...
// 		XXX another way to go here is to save the crop method and take 
// 			it into account when loading new sections of data...
//
// XXX should this set the .current to anything but null or the first elem???
function makeCroppedData(gids, keep_ribbons, keep_unloaded_gids){
	gids = fastSortGIDsByOrder(gids)
	var res = {
		varsion: DATA_VERSION,
		current: null,
		ribbons: [],
		order: DATA.order.slice(),
	}

	// remove any gid that is not in IMAGES or is not loaded...
	if(!keep_unloaded_gids){
		var loaded = []
		$.each(DATA.ribbons, function(i, e){ loaded = loaded.concat(e) })

		// NOTE: if IMAGES contains only part of the data loadable this will 
		// 		be wrong...
		gids = gids.filter(function(e){ 
			return e in IMAGES && loaded.indexOf(e) >= 0 
		})
	}

	// flat single ribbon crop...
	if(!keep_ribbons){
		res.ribbons[0] = gids

	// keep the ribbon structure...
	} else {
		$.each(DATA.ribbons, function(_, e){
			e = e.filter(function(ee){ return gids.indexOf(ee) >= 0 })
			// skip empty ribbons...
			if(e.length != 0){
				res.ribbons.push(e)
			}
		})
	}

	return res
}


// Crop data to given gids and set viewer state...
//
// Returns original state
//
// NOTE: if keep_ribbons is not set this will ALWAYS build a single ribbon
// 		data-set...
function cropDataTo(gids, keep_ribbons, keep_unloaded_gids){
	var prev_state = DATA
	var cur = DATA.current
	var r = keep_ribbons ? getRibbonIndex() : 0

	var new_data = makeCroppedData(gids, keep_ribbons, keep_unloaded_gids)

	// do nothing if there is no change...
	// XXX is there a better way to compare states???
	if(JSON.stringify(DATA.ribbons) == JSON.stringify(new_data.ribbons)){
		return DATA
	}

	CROP_STACK.push(prev_state)
	DATA = new_data

	cur = getGIDBefore(cur, r)
	cur = cur == null ? DATA.ribbons[r][0] : cur
	DATA.current = cur 

	$('.viewer').addClass('cropped-view')

	reloadViewer()
	updateImages()

	return prev_state
}


function uncropData(){
	if(!isViewCropped()){
		return DATA
	}
	var prev_state = DATA
	var cur = DATA.current

	DATA = CROP_STACK.pop()

	// check if cur exists in data being loaded...
	if($.map(DATA.ribbons, 
			function(e, i){ return e.indexOf(cur) >= 0 }).indexOf(true) >= 0){
		// keep the current position...
		DATA.current = cur
	}

	if(!isViewCropped()){
		$('.viewer').removeClass('cropped-view')
	}

	reloadViewer()
	updateImages()

	return prev_state
}


function showAllData(){
	var prev_state = DATA
	var cur = DATA.current

	if(CROP_STACK.length != 0){
		DATA = getAllData()
		CROP_STACK = []

		// XXX do we need to check if this exists???
		// 		...in theory, as long as there are no global destructive 
		// 		operations, no.
		// keep the current position...
		DATA.current = cur

		$('.viewer').removeClass('cropped-view')

		reloadViewer()
		updateImages()
	}

	return prev_state
}


// Helpers for making crop modes and using crop...

// Make a generic crop mode toggler
//
// NOTE: cls is optional...
function makeCropModeToggler(crop){
	var res = createCSSClassToggler(
			'.viewer',
			'cropped-view',
			function(action){
				if(action == 'on'){
					showStatusQ('Cropping ribbons...')
					crop()
				} else {
					showStatusQ('Uncropping all...')
					showAllData()
				}
			})
	return res
}



/**********************************************************************
* Modes... 
*/

// XXX make this not conflict with marked-only-mode, better yet, make them
// 		one single mode...
// XXX is this a mode???
var toggleSingleRibbonMode = makeCropModeToggler(
		function(){
				cropDataTo(getRibbonGIDs())
		})


function makeCurrenAndAboveModeToggler(keep_ribbons){
	return makeCropModeToggler(
		function(){
				var gids = []
				var c = getRibbonIndex()
				var ribbons = DATA.ribbons

				// merge the ribbons...
				for(var i=0; i <= c; i++){
					gids = gids.concat(ribbons[i])
				}
				gids.sort(imageOrderCmp)

				// do the crop...
				cropDataTo(gids, keep_ribbons)
		})
}
var toggleCurrenAndAboveRibbonMode = makeCurrenAndAboveModeToggler()
var toggleCurrenAndAboveRibbonsMode = makeCurrenAndAboveModeToggler(true)



/**********************************************************************
* Dialogs... 
*/

function cropImagesDialog(){

	updateStatus('Crop...').show()

	var alg = 'Crop ribbons: |'+
		'Use Esc and Shift-Esc to exit crop modes.'+
		'\n\n'+
		'NOTE: all crop modes will produce a single ribbon unless\n'+
		'otherwise stated.'

	cfg = {}
	cfg[alg] = [
		'Marked images', 
		'Marked images (keep ribbons)', 
		'Bookmarked images', 
		'Bookmarked images (keep ribbons)', 
		'Current ribbon', 
		'Current ribbon and above | Will merge the images into a single ribbon.',
		'Current ribbon and above (keep ribbons)'
	]

	formDialog(null, '', 
			cfg,
			'OK', 
			'cropImagesDialog')
		.done(function(res){
			res = res[alg]

			// NOTE: these must be in order of least-specific last...
			if(/Marked.*keep ribbons/.test(res)){
				var method = toggleMarkedOnlyWithRibbonsView

			} else if(/Marked/.test(res)){
				var method = toggleMarkedOnlyView

			} else if(/Bookmarked.*keep ribbons/i.test(res)){
				var method = toggleBookmarkedOnlyWithRibbonsView

			} else if(/Bookmarked/.test(res)){
				var method = toggleBookmarkedOnlyView

			} else if(/Current ribbon and above.*keep ribbons/.test(res)){
				var method = toggleCurrenAndAboveRibbonsMode

			} else if(/Current ribbon and above/.test(res)){
				var method = toggleCurrenAndAboveRibbonMode

			} else if(/Current ribbon/.test(res)){
				var method = toggleSingleRibbonMode
			}

			showStatusQ('Cropped: '+res+'...')

			method('on')
		})
		.fail(function(){
			showStatusQ('Crop: canceled.')
		})
}


function filterImagesDialog(){
	updateStatus('Filter...').show()

	cfg = {}
	cfg['GID |'
			+'Use gid or gid tail part to find an\n'
			+'image.\n'
			+'\n'
			+'NOTE: use of at least 6 characters is\n'
			+'recommended.'] = ''
	cfg['sep0'] = '---'
	cfg['Name'] = ''
	cfg['Path |'
			+'This applies to the non-common\n'
			+'part of the relative path only.'] = ''
	cfg['Comment'] = ''
	cfg['Tags |'
			+'An image will match if at least\n'
			+'one tag matches'] = ''
	// XXX date...
	cfg['Rotated'] = {select: [
		'',
		'no',
		'90&deg; or 270&deg;',
		'0&deg; or 180&deg;',
		'90&deg; only',
		'180&deg; only',
		'270&deg; only'
	]}
	cfg['Flipped'] = {select: [
		'',
		'no',
		'any',
		'vertical',
		'horizontal'
	]}
	cfg['sep1'] = '---'
	cfg['Marked'] = {select: [
		'',
		'yes',
		'no'
	]}
	cfg['Bookmarked'] = {select: [
		'',
		'yes',
		'no'
	]}
	cfg['sep2'] = '---'
	cfg['Ribbon'] = {select: [
		'all',
		'current only'
	]}
	cfg['Keep ribbons'] = false

	formDialog(null, 
			'Filter images |'
				+'All filter text fields support\n'
				+'regular expressions.\n'
				+'\n'
				+'Prepending a "!" to any text filter\n'
				+'will negate it, selecting unmatching\n'
				+'images only.\n'
				+'\n'
				+'Only non-empty fields are used\n'
				+'for filtering.',
			cfg,
			'OK', 
			'filterImagesDialog')
		.done(function(res){
			var gids

			showStatusQ('Filtering...')

			// XXX date...

			var filter = {}
			// build the filter...
			for(var field in res){
				// this will search for gid or gid part at the end of a gid...
				if(/^GID/.test(field) && res[field].trim() != ''){
					filter['id'] = res[field] + '$'

				} else if(/^Name/.test(field) && res[field].trim() != ''){
					filter['name'] = res[field]

				} else if(/^Path/.test(field) && res[field].trim() != ''){
					filter['path'] = res[field]

				} else if(/^Comment/.test(field) && res[field].trim() != ''){
					filter['comment'] = res[field]

				} else if(/^Tags/.test(field) && res[field].trim() != ''){
					filter['tags'] = res[field]

				} else if(/^Rotated/.test(field) && res[field].trim() != ''){
					if(res[field] == 'no'){
						filter['orientation'] = '^0$|undefined|null'
					} else if(/or/.test(res[field])){
						filter['orientation'] = res[field]
							.split('or')
							.map(function(e){
								e = parseInt(e)
								if(e == 0){
									return '^0$|undefined|null'
								}
								return e
							})
							.join('|')
					} else {
						filter['orientation'] = RegExp(parseInt(res[field]))
					}

				} else if(/^Flipped/.test(field) && res[field].trim() != ''){
					if(res[field] == 'no'){
						filter['flipped'] = 'undefined|null'
					} else if(res[field] == 'any'){
						filter['flipped'] = '.*'
					} else {
						filter['flipped'] = res[field]
					}

				} else if(/^Bookmarked/.test(field) && res[field].trim() != ''){
					if(res[field] == 'yes'){
						gids = getBookmarked(gids)
					} else {
						gids = getUnbookmarked(gids)
					}

				} else if(/^Marked/.test(field) && res[field].trim() != ''){
					if(res[field] == 'yes'){
						gids = getMarked(gids)
					} else {
						gids = getUnmarked(gids)
					}

				} else if(/^Ribbon/.test(field) && res[field].trim() != 'all'){
					if(res[field] == 'current only'){
						gids = getRibbonGIDs(gids)
					}
				}
			}

			var keep_ribbons = res['Keep ribbons']

			gids = filterGIDs(filter, gids)

			if(gids.length > 0){
				cropDataTo(gids, keep_ribbons)
			} else {
				showStatusQ('Filter: nothing matched.')
			}
		})
		.fail(function(){
			showStatusQ('Filter: canceled.')
		})
}



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

function setupCrop(viewer){
	return viewer
		// reset the crop stack when loading something...
		.on('fileStateLoaded', function(){
			CROP_STACK = []
		})
}
SETUP_BINDINGS.push(setupCrop)



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