/**********************************************************************
* 
*
*
**********************************************************************/
//var DEBUG = DEBUG != null ? DEBUG : true
/**********************************************************************
* Modal dialogs...
*/
/********************************************************* Helpers ***/
// Set element text and tooltip
//
// NOTE: when text is a list, we will only use the first and the last 
// 		elements...
// NOTE: if tip_elem is not given then both the text and tip will be set
// 		on text_elem
//
// XXX add support for quoted '|'...
function setTextWithTooltip(text, text_elem, tip_elem){
	text_elem = $(text_elem)
	tip_elem = tip_elem == null ? text_elem : tip_elem
	if(typeof(text) != typeof('str')){
		tip = text
	} else {
		var tip = text.split(/\s*\|\s*/)
	}
	// set elemnt text...
	text_elem
		.html(tip[0])
	// do the tooltip...
	tip = tip.slice(1)
	tip = tip[tip.length-1]
	if(tip != null && tip.trim().length > 0){
		$(' * ')
			.attr('tooltip', tip)
			.appendTo(tip_elem)
	}
	return text_elem
}
function getOverlay(root){
	root = $(root)
	var overlay = root.find('.overlay-block')
	if(overlay.length == 0){
		return $('
').appendTo(root)
	}
	return overlay
}
function showInOverlay(root, data){
	root = $(root)
	var overlay = getOverlay(root)
	
	if(data != null){
		var container = $('')
		var dialog = container.find('.dialog')
		//overlay.find('.background')
		//	.click(function(){ hideOverlay(root) })
		dialog
			.append(data)
			.on('click', function(evt){ 
				evt.stopPropagation() 
			})
		overlay.find('.content')
			.on('click', function(){ 
				overlay.trigger('close')
				hideOverlay(root) 
			})
			.on('close accept', function(){
				//hideOverlay(root) 
			})
			.append(container)
	}
	root.addClass('overlay')
	return overlay
}
function hideOverlay(root){
	root.removeClass('overlay')
	root.find('.overlay-block')
		.trigger('close')
		.remove()
}
function isOverlayVisible(root){
	return getOverlay(root).css('display') != 'none'
}
/**********************************************************************
* Field definitions...
*/
var FIELD_TYPES = {
	// a simple hr...
	//
	// format:
	// 		'---'
	// 		Three or more '-'s
	hr: {
		type: 'hr',
		text: null,
		default: false,
		html: '
	// 	}
	html: {
		type: 'html',
		text: null,
		default: false,
		html: '
',
		test: function(val){
			return val.html != null
		},
		set: function(field, value){
			if(typeof(value.html) == typeof('str')){
				field.html(value.html)
			} else {
				field.append(value.html)
			}
		},
	},
	// format: 
	// 		string
	// XXX add datalist option...
	// XXX make this textarea compatible...
	text: {
		type: 'text',
		text: null,
		default: '',
		html: ''+
				'
',
		test: function(val){
			return typeof(val) == typeof('abc')
		},
		set: function(field, value){
			$(field).find('.value').attr('value', value) 
		},
		get: function(field){ 
			return $(field).find('.value').attr('value') 
		},
	},	
	// format: 
	// 		true | false
	bool: {
		type: 'bool',
		text: null,
		default: false,
		html: ''+
				'
',
		test: function(val){
			return val === true || val === false
		},
		set: function(field, value){
			if(value){
				$(field).find('.value').attr('checked', '') 
			} else {
				$(field).find('.value').removeAttr('checked') 
			}
		},
		get: function(field){ 
			return $(field).find('.value').attr('checked') == 'checked'
		},
	},
	// NOTE: this will not work without node-webkit...
	// format: 
	// 		{ dir:  }
	dir: {
		type: 'dir',
		text: null,
		default: false,
		html: ''+
				'
',
		test: function(val){
			return typeof(val) == typeof({}) && 'dir' in val
		},
		set: function(field, value){
			field.find('.value').attr('nwworkingdir', value.dir)
		},
		get: function(field){ 
			var f = $(field).find('.value')[0].files
			if(f.length == 0){
				return ''
			}
			return f[0].path
		},
	},
	// NOTE: this will not work without node-webkit...
	// format: 
	// 		{ dir:  }
	// XXX add datalist option...
	ndir: {
		type: 'ndir',
		text: null,
		default: false,
		html: ''+
				'Browse '+
			'
',
		test: function(val){
			return typeof(val) == typeof({}) && 'ndir' in val
		},
		set: function(field, value){
			var that = this
			// NOTE: we are attaching the file browser to body to avoid 
			// 		click events on it closing the dialog...
			// 		...for some reason stopPropagation(...) does not do 
			// 		the job...
			var file = $(', 
					// 		enter, enter)...
					field.find('.path')
						.focus()
						.select()
				})
				.hide()
			field.find('.path').val(value.ndir)
			field.find('.browse').click(function(){
				file
					// load user input path...
					.attr('nwworkingdir', field.find('.path').val())
					.appendTo($('body'))
					.click()
			})
		},
		get: function(field){ 
			return field.find('.path').val()
		},
	},
	// format: 
	// 		['a', 'b', 'c', ...]
	//
	// an item can be of the folowing format:
	// 		 ['|' 'default' | 'disabled' ] [ '|'  ]
	//
	// NOTE: only one 'default' item should be present.
	// NOTE: if no defaults are set, then the first item is checked.
	choice: {
		type: 'choice',
		text: null,
		default: false,
		html: '',
		test: function(val){
			return typeof(val) == typeof([]) && val.constructor.name == 'Array'
		},
		set: function(field, value){
			var t = field.find('.text').html()
			t = t == '' ? Math.random()+'' : t
			var item = field.find('.item').last()
			for(var i=0; i < value.length; i++){
				// get options...
				var opts = value[i]
					.split(/\|/g)
					.map(function(e){ return e.trim() })
				var val = item.find('.value')
				val.val(opts[0])
				// set checked state...
				if(opts.slice(1).indexOf('default') >= 0){
					val.prop('checked', true)
					opts.splice(opts.indexOf('default'), 1)
				} else {
					val.prop('checked', false)
				}
				// set disabled state...
				if(opts.slice(1).indexOf('disabled') >= 0){
					val.prop('disabled', true)
					opts.splice(opts.indexOf('disabled'), 1)
					item.addClass('disabled')
				} else {
					val.prop('disabled', false)
					item.removeClass('disabled')
				}
				setTextWithTooltip(opts, item.find('.item-text'))
				item.appendTo(field)
				item = item.clone()
			}
			var values = field.find('.value')
				.attr('name', t)
			// set the default...
			if(values.filter(':checked:not([disabled])').length == 0){
				values.filter(':not([disabled])').first()
					.prop('checked', true)
			}
		},
		get: function(field){ 
			return $(field).find('.value:checked').val()
		},
	},
	// format: 
	// 	{ 
	// 		select: ['a', 'b', 'c', ...] 
	// 		// default option (optional)...
	// 		default:  | 
	// 	}
	select: {
		type: 'select',
		text: null,
		default: false,
		html: ''+
				''+
					' '+
			'
',
		test: function(val){
			return 'select' in val
		},
		set: function(field, value){
			var t = field.find('.text').text()
			var item = field.find('.option').last()
			var select = field.find('select')
			for(var i=0; i < value.select.length; i++){
				item
					.html(value.select[i])
					.val(value.select[i])
				item.appendTo(select)
				item = item.clone()
			}
			if(value.default != null){
				if(typeof(value.default) == typeof(123)){
					field.find('.option')
						.eq(value.default)
							.attr('selected', '')
				} else {
					field.find('.option[value="'+ value.default +'"]')
						.attr('selected', '')
				}
			}
		},
		get: function(field){ 
			return $(field).find('.option:selected').val()
		},
	},
	// NOTE: a button can have state...
	// format: 
	// 	{ 
	// 		// click event handler...
	// 		button: , 
	// 		// optional, button text (default 'OK')...
	// 		text: ,
	// 		// optional, initial state setup...
	// 		default: ,
	// 	}
	button: {
		type: 'button',
		text: null,
		default: false,
		html: ''+
				'
',
		test: function(val){
			return 'button' in val
		},
		set: function(field, value){
			var btn = $(field).find('button')
				.click(value.button)
				.html(value.text == null ? 'OK' : value.text)
			if('default' in value){
				value.default(btn)
			}
		},
		get: function(field){ 
			return $(field).attr('state')
		},
	},
}
/**********************************************************************
* Constructors...
*/
// Show a complex form dialog
//
// This will build a form and collect it's data on "accept" specified by
// the config object...
//
// config format:
//	{
//		// simple field...
//		: ,
//
//		...
//	}	
//
//  and split in two with a "|" the section before will
// show as the field text and the text after as the tooltip.
// Example:
// 		"field text | field tooltip..."
//
// field's default value determines it's type:
// 	bool		- checkbox
// 	string		- textarea
//
// see FIELD_TYPES for supported field types.
//
// NOTE: if btn is set to false explicitly then no button will be 
// 		rendered in the form dialog.
// NOTE: to include a literal "|" in  just escape it
// 		like this: "\|"
//
// XXX add form testing...
// XXX add undefined field handling/reporting...
function formDialog(root, message, config, btn, cls){
	cls = cls == null ? '' : cls
	btn = btn == null ? 'OK' : btn
	root = root == null ? $('.viewer') : root
	var form = $('
')
	var data = {}
	var res = $.Deferred()
	// handle message and btn...
	if(message.trim().length > 0){
		setTextWithTooltip(message, $('
'))
			.appendTo(form)
	}
	// build the form...
	for(var t in config){
		var did_handling = false
		for(var f in FIELD_TYPES){
			if(FIELD_TYPES[f].test(config[t])){
				var field = FIELD_TYPES[f]
				var html = $(field.html)
				// setup text and data...
				setTextWithTooltip(t, html.find('.text'), html)
				if(field.set != null){
					field.set(html, config[t])
				}
				if(field.get != null){
					// NOTE: this is here to isolate t and field.get values...
					// 		...is there a better way???
					var _ = (function(title, getter){
						html.on('resolve', function(evt, e){
							data[title] = getter(e)
						})
					})(t, field.get)
				}
				form.append(html)
				did_handling = true
				break
			}
		}
		// handle unresolved fields...
		if(!did_handling){
			console.warn('formDialog: not all fields understood.')
			// XXX skipping field...
			// XXX
		}
	}
	// add button...
	if(btn !== false){
		var button = $(''+btn+' ')
		form.append(button)
	} else {
		var button = null
	}
	var overlay = showInOverlay(root, form)
		.addClass('dialog ' + cls)
		.on('accept', function(){
			form.find('.field').each(function(_, e){
				$(e).trigger('resolve', [$(e)])
			})
			// XXX test if all required stuff is filled...
			res.resolve(data, form)
			hideOverlay(root)
		})
		.on('close', function(){
			res.reject()
		})
	if(button != null){
		button.click(function(){
			overlay.trigger('accept')
		})
	}
	// focus an element...
	// NOTE: if first element is a radio button set, focus the checked
	//		element, else focus the first input...
	form.ready(function(){ 
		// NOTE: we are using a timeout to avoid the user input that opened
		// 		the dialog to end up in the first field...
		setTimeout(function(){
			var elem = form.find('.field input').first()
			if(elem.attr('type') == 'radio'){
				form.find('.field input:checked')
					.focus()
					.select()
			} else {
				elem
					.focus()
					.select()
			}
		}, 100)
	})
	return res
}
/************************************************ Standard dialogs ***/
// NOTE: these return a deferred that will reflect the state of the 
// 		dialog, and the progress of the operations that it riggers...
//
// XXX might be a good idea to be able to block the ui (overlay + progress
// 		bar?) until some long/critical operations finish, to prevent the
// 		user from breaking things while the ui is inconsistent...
function alertDialog(){
	var message = $.makeArray(arguments).join(' ')
	return formDialog(null, String(message), {}, false, 'alert')
}
function promptDialog(message, dfl, btn){
	btn = btn == null ? 'OK' : btn
	var res = $.Deferred()
	formDialog(null, message, {'': ''+(dfl == null ? '' : dfl)}, btn, 'prompt')
		.done(function(data){ res.resolve(data['']) })
		.fail(function(){ res.reject() })
	return res
}
/*
function confirmDialog(){
}
*/
/**********************************************************************
* vim:set ts=4 sw=4 :                                                */