/*
PiAnoS (Picture Annotation System), A fingerprint examiner training tool
Copyright (C) 2011-2012 University of Lausanne, Switzerland
https://ips-labs.unil.ch/pianos

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see http://www.gnu.org/licenses/.
*/

Pianos4Backend.CreateExercise = function(selector, params)
{	
	params = params || {};
	if (!(!params.folder != !params.id)) udb.error('Programming error', 'One of the folder ID or the exercise ID must be specified'); // The conditional is !(a XOR b), where XOR is achieved by !a != !b (http://www.howtocreate.co.uk/xor.html)
	if (!params.folder) params.folder = null; else params.folder = parseInt(params.folder);
	if (!params.id) params.id = null; else params.id = parseInt(params.id);
	if (!params.noImageURL) params.noImageURL = 'css/no_image_selected.png';
	if (!params.onCreate) params.onCreate = function(id) { alert('Created exercise ID '+id); };
	if (!params.onSave) params.onSave = function(id) { alert('Exercise ID '+id+' was saved'); };
	if (!params.user) udb.error('Programming error', 'The user must be specified');
	if (typeof params.clone == 'undefined') params.clone = false;
	
	this.init = function()
	{
		buildUI();
	}
	
	var self = this;
	var frozen = 0;
	var uploaders = [];

	var buildUI = function()
	{
			
		var rids = [];
		for (var i=0;i<4;++i) rids.push('create_ex_'+(new Date().getTime())+'_'+i);
		
		var form = $('<form />').attr('action','?')
			.addClass('pianos4_main_form')
			.append($('<div />')
				.append($('<label />').attr('for',rids[0]).text('Name'))
				.append($('<input />').attr('id',rids[0]).attr('type','text').attr('name','name')))
			.append($('<div />')
				.append($('<label />').attr('for',rids[1]).text('Description'))
				.append($('<textarea />').attr('id',rids[1]).attr('rows',6).attr('cols',30).attr('name','description')))
			.append($('<div />')
				.append($('<label />').attr('for',rids[2]).text('Mark image'))
				.append($('<input />').attr('id',rids[2]).attr('type','hidden').attr('name','mark'))
				.append(createImageLoader(rids[2], 'MARK', params.clone)))
			.append($('<div />')
				.append($('<label />').attr('for',rids[3]).text('Print image'))
				.append($('<input />').attr('id',rids[3]).attr('type','hidden').attr('name','print'))
				.append(createImageLoader(rids[3], 'PRINT')))
			.append($('<div />')
				.append($('<input />').attr('type','submit').attr('value',params.id ? 'Save' : 'Create').button()))
		;
		
		form.bind('submit', onSubmit);
		
		var metaname = params.user.tutor ? 'exercise' : 'case';
		
		selector
			.addClass('pianos4_main')
			.append($('<h2 />').text(params.id ? 'Edit '+metaname+' '+params.id : 'Create a new '+metaname))
			.append(form)
		;
		
		selector.find('a[href=#upload]').each(function() {	
			var $this = $(this);
			var uploader = new AjaxUpload($this.attr('id'), {
				action: '?action=upload_image',
				name: 'images[]',
				data: {
					type : $this.data('image_type'),
				},				
				autoSubmit: true,
				responseType: 'json',
				onComplete: function(file, response) {
					log(response);
					if (response.error)
					{
						udb.error('Error while uploading image', response.message);
					}
					else
					{
						selector.find('#'+$this.data('target_id')).val(response.ret);
						$this.parents('.pianos4_image_options').siblings('.pianos4_image_preview').children('img').attr('src','?action=get_image&thumb&id='+response.ret);
					}
				}
			});
			uploaders.push(uploader);
		});
		
		if (params.id)
		{
			freeze();
			
			if (params.clone)
			{
				udb.app().getModel().rpc('getExerciseCloneData', {id:params.id}, function(ex) {
					var data = $.extend(true, {}, ex);
					data.name = 'Copy of '+data.name;
					data.description = '(Was: '+data.description+')';
					data.print = null;
					setData(data);
					selector.find('.pianos4_image_options:first').html('')
						.append($('<p />')
							.css('margin-left','1em')
							.text('Analysis is the same as ')
							.append($('<a />')
								.attr('href','#exercise/'+ex.id)
								.attr('target','_blank')
								.addClass('clone_exercise_info')
								.data('pianos4.clone_aid', ex.progress.aid)
								.text(ex.name)
							)
						);
					
					thaw();
				});
			}
			else
			{
				udb.app().getModel().rpc('getExercise', {id:params.id}, function(ex) {
					setData(ex);
					thaw();
				});
			}
		}
	}
	
	var createImageLoader = function(id, imgType, readOnly)
	{
		var sp1 = $('<span />')
			.addClass('pianos4_image_preview')
			.append($('<img />').attr('src', params.noImageURL).attr('alt','No image selected'))
		;
		
		var sp2 = $('<span />').addClass('pianos4_image_options');
		
		if (readOnly)
		{
			sp2.html('<p style="margin-left:1em;color:#666">Loading ...</p>');
		}
		else
		{
			var rid = 'uploader_'+(new Date().getTime());
			sp2.append($('<ul />')
					.append($('<li />').append($('<a href="#library" />').text('Pick an image from the library').click(function(e) {
						e.preventDefault();
						if (frozen) return false;
						var il = new Pianos4Backend.ImageLibrary({
							onSelect: function(image) {
								$('#'+id).val(image.id);
								$('#'+id).siblings('span').find('.pianos4_image_preview img').attr('src','?action=get_image&thumb&id='+image.id);
							},
							typefilter: imgType,
						});
					})))
					.append($('<li />').append($('<a href="#upload" />').attr('id',rid).data('target_id',id).data('image_type', imgType).text('Upload a new image').click(function(e) {
						e.preventDefault();
					})))
				)
			;
		}
		
		return $('<span />').append(sp1).append(sp2);
	}
	
	var onSubmit = function(e)
	{	
		e.preventDefault();
		var errors = [];
		var data = $(this).serializeObject();
		log(data);
		if (data.name.length == 0)
			errors.push('The name must not be empty');
		if (data.description.length == 0)
			errors.push('The description must not be empty');
		data.mark = parseInt(data.mark);
		if (isNaN(data.mark) || data.mark <= 0)
			errors.push('You must choose a mark image');
		data.print = parseInt(data.print);
		if (isNaN(data.print) || data.print <= 0)
			errors.push('You must choose a print image');
		
		if (errors.length)
		{
			udb.error('Could not create the exercise', errors.join('\n'));
		}
		else
		{
			if (params.clone)
			{
				data.aid = $('.clone_exercise_info').data('pianos4.clone_aid');
				assert(typeof data.aid == 'number' && data.aid > 0);
				udb.app().getModel().rpc('cloneExercise', data, function(id) {
					if (params.onCreate)
						params.onCreate(id);
				});
			}
			else
			{
				if (params.id)
				{
					data.id = params.id;
					udb.app().getModel().rpc('saveExercise', data, function() {
						if (params.onSave)
							params.onSave(params.id);
					});
				}
				else
				{
					data.folder = params.folder;
					udb.app().getModel().rpc('createExercise', data, function(id) {
						if (params.onCreate)
							params.onCreate(id);
					});
				}
			}
		}
	}
	
	var freeze = function()
	{
		assert(frozen >= 0);
		if (++frozen > 0)
		{
			selector.find('input:not([type=submit])').prop('disabled',true);
			selector.find('input[type=submit]').button('disable');
			selector.find('textarea').prop('disabled',true);
			for (var i in uploaders) uploaders[i].disable();
		}
	}

	var thaw = function()
	{
		assert(frozen > 0);
		if (--frozen == 0)
		{
			selector.find('input:not([type=submit])').prop('disabled',false);
			selector.find('input[type=submit]').button('enable');
			selector.find('textarea').prop('disabled',false);
			for (var i in uploaders) uploaders[i].enable();
		}
	}
	
	var setData = function(ex)
	{
		assert(parseInt(ex.id) === params.id);
		selector.find('input[name=name]').val(ex.name);
		selector.find('textarea[name=description]').val(ex.description);
		selector.find('input[name=mark]').val(ex.mark)
			.siblings('span').find('.pianos4_image_preview img')
				.attr('src', ex.mark ? '?action=get_image&thumb&id='+ex.mark : params.noImageURL);
		selector.find('input[name=print]').val(ex.print)
			.siblings('span').find('.pianos4_image_preview img')
				.attr('src', ex.print ? '?action=get_image&thumb&id='+ex.print : params.noImageURL);
	}
}

Pianos4Backend.ImageLibrary = function(params)
{	
	params = params || {};
	if (!params.title) params.title = 'Image Library';
	
	var self = this;
	var panel = null;

	(function() {
		var div = $('<div />');
		
		var newp = $.extend(true, {}, params);
		newp.onSelect = function(id) 
		{
			div.dialog('close');
			if (params.onSelect)
				params.onSelect(id);
		};
		
		panel = new Pianos4Backend.ImageLibraryPanel(div, newp);
		panel.init();
		
		div.dialog({
			autoOpen: true,
			modal: true,
			width: $(window).width()*80.0/100.0,
			height: $(window).height()*80.0/100.0,
			title: params.title,
			buttons: {
				'Close': function() {
					$(this).dialog('close');
				},
			},
			close: function() {
				$(this).remove();
			},
		});
	})();
}

Pianos4Backend.ImageLibraryPanel = function(parent, params)
{	
	params = params || {};
	if (!params.onSelect) params.onSelect = function(image) { alert('Selected image ID '+image.id); };
	if (!params.typefilter) params.typefilter = '*';
	if (!params.prompt) params.prompt = 'Please choose an image';
	
	this.init = function()
	{
		buildUI();
		load(true);
	}
	
	var self = this;
	var content = null;
	var lastLoadOptions = null;
	var loading = false;
	
	var buildUI = function()
	{
		content = $('<div />').addClass('image_library');
		
		var header = $('<div />').addClass('image_library_header')

		var rids = [];
		for (var i=0;i<2;++i) rids.push('image_lib_'+(new Date().getTime())+'_'+i);
		var form = $('<form />')
				.append($('<label />').attr('for',rids[0]).text('Filter'))
				.append($('<input />').attr('id',rids[0]).attr('type','text').attr('name','filter').bind('change', function() { load(); }))
				.append($('<select />').attr('id',rids[0]).attr('name','type').bind('change', function() { load(); })
					.append($('<option />').attr('value','*').text('All types'))
					.append($('<option />').attr('value','MARK').text('Marks only'))
					.append($('<option />').attr('value','PRINT').text('Prints only')))
		;
		header.append($('<span />').text(params.prompt)).append(form);

		form.find('select[name=type]').val(params.typefilter);
		
		var div = $('<div />').addClass('image_library_images');
		
		content.append(header).append(div);
		
		content.appendTo(parent);
	};
	
	var load = function(force)
	{
		var div = content.find('.image_library_images');
		div.html('').append($('<div />').append($('<img class="loading" src="css/throbber-18-black-on-white.gif" alt="Loading ..." />')).append($('<span />').text('Loading ...')));
		
		var opts = {};
		var filter = content.find('input[name=filter]').val();
		if (filter.length > 1)
			opts.filter = filter;
		var type = content.find('select[name=type]').val();
		if (type == 'MARK' || type == 'PRINT')
			opts.type = type;
			
		if (!force || lastLoadOptions)
		{
			var differ = false;
			var compare = ['filter','type'];
			for (var i in compare) 
			{
				var cmp = compare[i];
				if ((cmp in lastLoadOptions && !(cmp in opts)) || (cmp in opts && !(cmp in lastLoadOptions)))
				{
					differ = true;
					break;
				}
				else if (cmp in lastLoadOptions && cmp in opts && lastLoadOptions[cmp] != opts[cmp])
				{
					differ = true;
					break;
				}
			}
			
			if (!differ)
				return;
		}

		loading = true;
		udb.app().getModel().rpc('getImageLibrary', opts, function(images) {
			loading = false;
			lastLoadOptions = opts;
			div.html('');
			for (var i in images) (function(image)
			{
				div.append($('<span />')
					.addClass('image_library_image')
						.append($('<span />').addClass('pianos4_image_preview')
						.append($('<img />').attr('src', '?action=get_image&thumb&id='+image.id).attr('alt',image.name)))
					.append($('<span />')
						.addClass('image_library_legend')
						.text(image.name))
					.click(function() {
						params.onSelect(image);
					})
				);
				
			})(images[i]);
		});
	};
}

Pianos4Backend.AdminHome = function(selector, params)
{	
	params = params || {};
	if (!params.user) udb.error('Programming error', 'The user must be specified');
	
	this.init = function()
	{
		buildUI();
		log(params.user);
	}
	
	var self = this;

	var buildUI = function()
	{
		var div = $('<div />')
			.append($('<h2 />').text('Welcome to PiAnoS, '+params.user.name))
		;
		selector
			.addClass('pianos4_main')
			.append(div);
	}
}
		
Pianos4Backend.AdminExercise = function(selector, params)
{	
	params = params || {};
	if (!params.id) udb.error('Programming error', 'The exercise ID must be specified');
	if (typeof params.showResultsPreview == 'undefined') params.showResultsPreview = false;
	
	this.init = function()
	{
		buildUI();
	}
	
	var self = this;

	var buildUI = function()
	{
		udb.app().getModel().rpc('getExercise', {id: params.id, withAssignments:true}, function(ex) {
			log(ex);

			var div = $('<div />')
				.append($('<h2 />').text(ex.name));
				
			var info = $('<div />').addClass('ui-widget pianos4_frame').appendTo(div)
				.append($('<div />').addClass('ui-widget-header').addClass('ui-corner-top').text('General information'))
				.append($('<div />').addClass('ui-widget-content').addClass('ui-corner-bottom')
					.append($('<p />').addClass('exercise_description').text(ex.description))
					.append($('<span />').addClass('pianos4_image_preview')
						.append($('<img />').attr('src', '?action=get_image&thumb&id='+ex.mark).attr('alt','Mark image')))
					.append($('<span />').addClass('pianos4_image_preview')
						.append($('<img />').attr('src', '?action=get_image&thumb&id='+ex.print).attr('alt','Print image')))
					.append($('<div />').css('font-size','0.8em').css('margin-bottom','1em')
						.append($('<a />').attr('href','?action=get_image&id='+ex.mark).text('Download mark image'))
						.append($('<span />').css('display','inline-block').css('width','30px'))
						.append($('<a />').attr('href','?action=get_image&id='+ex.print).text('Download print image')))
					.append($('<div />')
						.append($('<a />').attr('href','?action=take&id='+ex.id).text('Review results').button())
						.append($('<a />').attr('href','?#exercise/'+ex.id+'/edit').text('Edit').css('margin-left','1em').button()))
				)
			;
		
			if (params.showResultsPreview)
			{
				var frame = info.find('.ui-widget-content:first');
				frame.css('position','relative');
				var d = $('<div />').css('position','absolute').width('300px').height('100%').css('top','0').css('right','1em').appendTo(frame);
				setTimeout(function() {
					d.html('');
					var iframe = new Pianos4Backend.IFrame($('<div />').appendTo(d), params.id, true, true);
					iframe.init();
				}, 100);
			}
			
			var resdiv = $('<div />');
			
			var aplural = ex.assignments.length == 1 ? 'result' : 'results';
			var results = $('<div />').addClass('ui-widget pianos4_frame').appendTo(div)
				.append($('<div />').addClass('ui-widget-header ui-corner-top').text('Results'))
				.append($('<div />').addClass('ui-widget-content ui-corner-bottom')
					.append(resdiv)
				)
			;

			var findLatestResultIndex = function(data, step)
			{
				var stepidx = -1;
				for (var i=0;i<data.numresults;++i)
				{
					var phase = data.ressteps[i];
					if (phase != step)
						continue;
					else
						stepidx = i;
				}
				return stepidx;
			}
			
			var nl2br = new RegExp(/\n/g);
			
			var writeDataCell = function(data, stepidx, cell)
			{
				var concl = null;
				if (stepidx >= 0)
				{
					try {
						var jsonSrc = data.conclusions[stepidx];
						concl = JSON.parse(jsonSrc);
					} catch (e) {
						concl = null;
					}
				}

				if (concl)
				{
					var lines = concl.split(nl2br);
					for (var i in lines)
						cell.append($('<div />').text(lines[i]));
				}
				else
				{
					cell.css('font-style', 'italic').text('No data');
				}
			}
			
			resdiv.qtable({
				data: ex.assignments,
				columnMode: 'mapping',
				columns: [
					{
						title: 'User',
						render: function(data, i, j, cell) {
							cell.append($('<a />').attr('href','#person/'+data[i].personid).text(data[i].person));
						},
					},{
						title: 'Status',
						render: function(data, i, j, cell) {
							cell.text(data[i].progress.label);
						},
					},{
						title: 'Analysis',
						render: function(data, i, j, cell) {	
							var d = data[i];
							var step = findLatestResultIndex(d, 'Analysis');
							writeDataCell(d, step, cell);
						},
					},{
						title: 'Conclusion',
						render: function(data, i, j, cell) {
							var d = data[i];
							var step = findLatestResultIndex(d, 'Comparison');
							writeDataCell(d, step, cell);
						},
					},{
						title: '',
						render: function(data, i, j, cell) {
							cell.append(
								$('<img />')
									.attr('src','css/icons/backend/Delete16.png')
									.attr('alt','delete')
									.css('cursor','pointer')
									.click(function() {
										$('<div />').text('Do you really want to delete these results ?').dialog({
											title: "Confirm results deletion",
											modal: true,
											buttons: {
												'OK': function() {
													var dlg = $(this);
													udb.app().getModel().rpc('deleteAssignment',{aid:parseInt(data[i]['id'])},function() {
														cell.parents('tr').remove();
														dlg.dialog('close');
													});
												},
												'Cancel': function() { 
													$(this).dialog('close');
												},
											},
											close: function() { 
												$(this).remove();
											}
										});		
										
									})
							);
						},
					}
				],
			});
			
			selector
				.addClass('pianos4_main')
				.append(div);
		});
	}
}

Pianos4Backend.AdminUser = function(selector, params)
{	
	params = params || {};
	if (!params.id) udb.error('Programming error', 'The user ID must be specified');
	
	this.init = function()
	{
		buildUI();
	}
	
	var self = this;

	var buildUI = function()
	{
		udb.app().getModel().rpc('getUser', {id: params.id, withAssignments: true}, function(user) {
			log(user);

			var div = $('<div />')
				.append($('<h2 />').text(user.name));
				
			var displv = function(label, value) {
				return $('<p />').css('margin','0.3em 0')
					.append($('<span />').css('display','inline-block').css('width','12em').text(label))
					.append($('<span />').text(value));
			};
					
			var info = $('<div />').addClass('ui-widget pianos4_frame').appendTo(div)
				.append($('<div />').addClass('ui-widget-header').addClass('ui-corner-top').text('General information'))
				.append($('<div />').addClass('ui-widget-content').addClass('ui-corner-bottom')
					.append(displv('active',user.active ? 'yes' : 'no'))
					.append(displv('username',user.username))
					.append(displv('tutor',user.tutor ? 'yes' : 'no'))
					.append(displv('email',user.email))
					.append(displv('groups',[{name:[]}].concat(user.groups).reduce(function(a,b) {return {name:a.name.concat(b.name)};}).name.join(', ')))
							// In Python I would have written ', '.join([x.name for x in user.groups]) but hey, this is Javascript.
					
					/*.append($('<div />')
						.append($('<a />').attr('href','?#exercise/'+user.id+'/edit').text('Edit').button()))*/
				)
			;
		
			var resdiv = $('<div />');
			
			var results = $('<div />').addClass('ui-widget pianos4_frame').appendTo(div)
				.append($('<div />').addClass('ui-widget-header ui-corner-top').text('Results'))
				.append($('<div />').addClass('ui-widget-content ui-corner-bottom')
					.append(resdiv)
				)
			;

			var findLatestResultIndex = function(data, step)
			{
				var stepidx = -1;
				for (var i=0;i<data.numresults;++i)
				{
					var phase = data.ressteps[i];
					if (phase != step)
						continue;
					else
						stepidx = i;
				}
				return stepidx;
			}
			
			var nl2br = new RegExp(/\n/g);
			
			var writeDataCell = function(data, stepidx, cell)
			{
				var concl = null;
				if (stepidx >= 0)
				{
					try {
						var jsonSrc = data.conclusions[stepidx];
						concl = JSON.parse(jsonSrc);
					} catch (e) {
						concl = null;
					}
				}

				if (concl)
				{
					var lines = concl.split(nl2br);
					for (var i in lines)
						cell.append($('<div />').text(lines[i]));
				}
				else
				{
					cell.css('font-style', 'italic').text('No data');
				}
			}
			
			resdiv.qtable({
				data: user.assignments,
				columnMode: 'mapping',
				columns: [
					{
						title: 'Exercise',
						render: function(data, i, j, cell) {
							cell.append($('<a />').attr('href','#exercise/'+data[i].exid).text(data[i].exname));
						},
					},{
						title: 'Status',
						render: function(data, i, j, cell) {
							cell.text(data[i].progress.label);
						},
					},{
						title: 'Analysis',
						render: function(data, i, j, cell) {	
							var d = data[i];
							var step = findLatestResultIndex(d, 'Analysis');
							writeDataCell(d, step, cell);
						},
					},{
						title: 'Conclusion',
						render: function(data, i, j, cell) {
							var d = data[i];
							var step = findLatestResultIndex(d, 'Comparison');
							writeDataCell(d, step, cell);
						},
					},{
						title: '',
						render: function(data, i, j, cell) {
							cell.append(
								$('<img />')
									.attr('src','css/icons/backend/Delete16.png')
									.attr('alt','delete')
									.css('cursor','pointer')
									.click(function() {
										$('<div />').text('Do you really want to delete these results ?').dialog({
											title: "Confirm results deletion",
											modal: true,
											buttons: {
												'OK': function() {
													var dlg = $(this);
													udb.app().getModel().rpc('deleteAssignment',{aid:parseInt(data[i]['id'])},function() {
														cell.parents('tr').remove();
														dlg.dialog('close');
													});
												},
												'Cancel': function() { 
													$(this).dialog('close');
												},
											},
											close: function() { 
												$(this).remove();
											}
										});		
										
									})
							);
						},
					}
				],
			});
			
			selector
				.addClass('pianos4_main')
				.append(div);
		});
	}
}
		
Pianos4Backend.AdminFolder = function(selector, params)
{	
	params = params || {};
	if (!params.id) udb.error('Programming error', 'The folder ID must be specified');
	if (!params.user) udb.error('Programming error', 'The user must be specified');
	
	this.init = function()
	{
		buildUI();
	}
	
	var self = this;
	var folder = null;
	
	var buildUI = function()
	{
		udb.app().getModel().rpc('getFolder', {id: params.id}, function(_folder) {
			
			folder = _folder;
			
			var div = $('<div />')
				.append($('<h2 />').text(folder.name))
			;

			acldiv = $('<div />').attr('id','pianos4_acldiv_'+params.id);
			
			var isPersonalFolder = folder.personalof && parseInt(folder.personalof,10) === params.user.id;
			
			var metaname = params.user.tutor || !isPersonalFolder ? 'exercise' : 'case';
			
			var explural = metaname + (folder.exercises.length == 1 ? '' : 's');
			var info = $('<div />').addClass('ui-widget pianos4_frame')
				.append($('<div />').addClass('ui-widget-header ui-corner-top').text('General information'))
				.append($('<div />').addClass('ui-widget-content ui-corner-bottom')
					.append($('<div />').text(folder.exercises.length+' '+explural+' in this folder'))
					.append(acldiv)
				).appendTo(div);
			
			if (params.user.tutor)
			{
				resetACLDiv();
				
				var cachedGroups = udb.app().cache('acl-groups');
				if (!cachedGroups)
				{
					udb.app().getModel().rpc('getUserGroups', {noAdmins:true,countUsers:true}, function(groups) {
						udb.app().cache('acl-groups', groups);
						showACL(folder.acl, groups);
					});
				}
				else
				{
					showACL(folder.acl, cachedGroups, acldiv);
				}
			}
			else
			{
				if (isPersonalFolder)
					info.find('.ui-widget-content').append($('<a />').attr('href','#createexercise/'+params.id).text('Create case here').button())
			}
			
			var exercises = $('<div />').addClass('ui-widget pianos4_frame')
				.append($('<div />').addClass('ui-widget-header ui-corner-top').text(metaname[0].toUpperCase()+metaname.substr(1)+'s'))
				.append($('<div />').addClass('ui-widget-content ui-corner-bottom')
				).appendTo(div);

			var excontainer = exercises.find('.ui-widget-content');

			var renderImage = function(data, i, j, cell, alt) {
				cell.append($('<span />').addClass('pianos4_image_preview')
					.append($('<img />').attr('src', '?action=get_image&thumb&id='+data[i][j]).attr('alt',alt)));
			};
			
			$('<div />').appendTo(excontainer).qtable({
				data: folder.exercises,
				columns: [
					/*{
						key: 'id',
						title: 'ID'
					},*/{
						key: 'mark',
						title: 'Mark image',
						render: function(a,b,c,d) { return renderImage(a,b,c,d,'Mark image'); },
					},{
						key: 'print',
						title: 'Print image',
						render: function(a,b,c,d) { return renderImage(a,b,c,d,'Print image'); },
					},{
						key: 'name',
						title: 'Name',
					},{
						key: 'description',
						title: 'Description',
					}
				],
				columnMode: 'mapping',
				cellWrap: function(data,i) { return $('<a />').addClass('qtable_cell_link').attr('href','#exercise/'+data[i].id) },
				trClass: 'qtable_row_link',
			});
			
			selector
				.addClass('pianos4_main')
				.append(div);
		});
	}
	
	var resetACLDiv = function() 
	{
		$('#pianos4_acldiv_'+params.id).html('<span>Loading user groups ...</span><span><img src="css/throbber-18-black-on-white.gif" alt="Please wait ..." /></span>');
	};
	
	var showACL = function(acl, allgroups, acldiv)
	{
		acldiv = acldiv || $('#pianos4_acldiv_'+params.id);
		
		var gmap = {};
		for (var i in allgroups)
		{
			var group = allgroups[i];
			group['id'] = parseInt(group['id']);
			gmap[group.id] = group.name;
		}

		var names = [];
		for (var i in acl)
			names.push(gmap[acl[i]]);
			
		var namesspan = $('<span />');
		if (names.length)
			namesspan.text(names.join(', '));
		else
			namesspan.css('font-style','italic').text('None');
		
		acldiv.html('')
			.append($('<div />')
				.append($('<span />').css('font-weight','bold').text('Allowed groups: '))
				.append(namesspan))
			.append($('<div />')
				.append($('<a />').attr('href','#').text('Change access rights').button().click(function(e) {
					e.preventDefault();
					showACLDialog(acl, allgroups, function(newacl) {
						resetACLDiv();
						udb.app().getModel().rpc('setFolderACL', {id:params.id, acl:newacl}, function() {
							showACL(newacl, allgroups);
						});
					});
				}))
				.append($('<a />').attr('href','#createexercise/'+params.id).css('margin-left','1em').text('Create exercise here').button())
				.append($('<a />').attr('href','#').css('margin-left','1em').text('Duplicate folder').button().click(function(e) {
					e.preventDefault();
					var div = $('<div />')
						.append($('<p />').text('Please enter a name for the new folder'))
						.append($('<p />').append($('<input />').attr('type','text').attr('name','name').css('width','20em').val('Copy of '+folder.name)))
					;
					div.dialog({
						title: 'Duplicate folder',
						modal: true,
						close: function() { $(this).remove(); },
						buttons: {
							'OK': function() { 
								var name = div.find('input[name=name]').val();
								var $this = $(this);
								udb.app().getModel().rpc('duplicateFolder', {id:folder.id, name:name}, function(folderID) {
									$this.dialog('close');
									window.location.hash = 'folder/'+folderID;
									udb.app().refreshTreePart('allex');
								});
							},
							'Cancel': function() { $(this).dialog('close'); },
						},
					});
				}))
			)
		;
	}

	var showACLDialog = function(acl, allgroups, callback)
	{
		var div = $('<div />');
		div.append($('<p />').text('Choose the user groups that have access to this folder.'));
		var ol = $('<ol />').css('list-style-type','none').css('padding','0').appendTo(div);
		for (var i in allgroups)
		{
			var group = allgroups[i];
			var li = $('<li />').text(group['name']).data('value',group['id']).css('padding','0.2em 0.5em').css('cursor','pointer');
			var usrplural = group['nummembers'] == 1 ? 'user' : 'users';
			li.append($('<span />').css('float','right').css('font-style','italic').css('font-size','0.9em').text('('+group['nummembers']+' '+usrplural+')'));
			var idx = $.inArray(group['id'], acl);
			if (idx >= 0)
				li.addClass('ui-state-highlight');
			else
				li.addClass('ui-state-default');
			li.click(function() {
				$(this).toggleClass('ui-state-highlight').toggleClass('ui-state-default');
			});
			ol.append(li);
		}
		div.append($('<p />')
			.append($('<a />').css('float','left').attr('href','#').text('Remove all').click(function(e) {
				e.preventDefault();
				div.find('li').removeClass('ui-state-highlight').addClass('ui-state-default');
			}))
			.append($('<a />').css('float','right').attr('href','#').text('Select all').click(function(e) {
				e.preventDefault();
				div.find('li').removeClass('ui-state-default').addClass('ui-state-highlight');
			}))
		);
		
		div.dialog({
			title: "Folder access control",
			modal: true,
			buttons: {
				'OK': function() {
					var newacl = [];
					$('li',this).each(function() {
						if ($(this).hasClass('ui-state-highlight'))
							newacl.push($(this).data('value'));
					});
					$(this).dialog('close');
					callback(newacl);
				},
				'Cancel': function() { 
					$(this).dialog('close');
				},
			},
			close: function() { 
				$(this).remove();
			}
		});		
	}
}
		
Pianos4Backend.ImageManager = function(parent, params)
{	
	params = params || {};
	
	this.init = function()
	{
		buildUI();
		load(true);
	}
	
	var self = this;
	var content = null;
	var loading = false;
	
	var buildUI = function()
	{
		content = $('<div />').addClass('pianos4_main')
			.append($('<h2 />').text('Images Management'))
			.append($('<div />'))
			.appendTo(parent);
	};
	
	var load = function(force)
	{
		var div = content.find('>div:first');
		loading = true;
		var opts = {};
		udb.app().getModel().rpc('getImageLibrary', {extended:true}, function(images) {
			loading = false;
			var map = {};
			for (var i in images)
			{
				var img = images[i];
				var m = {};
				var names = [];
				var ids = [];
				for (var j in img.ex_ids)
				{
					var exid = img.ex_ids[j];
					var name = img.ex_names[j]
					if (name != 'NULL' && !(exid in m))
					{
						ids.push(exid);
						names.push(name);
					}
				}
				map[i] = [ids,names];
			}
			div.qtable({
				data: images,
				columnMode: 'mapping',
				columns: [{
					title: '',
					render: function(data, i, j, cell) {
						var image = data[i];
						cell.append($('<img />').attr('src', '?action=get_image&thumb&id='+image.id).attr('alt',image.name).css('width','32px'));
					}
				},{
					title: 'Name',
					render: function(data, i, j, cell) {
						var image = data[i];
						log(image);
						log(name);
						cell.append($('<a />').attr('href','?action=get_image&id='+image.id).text(image.name));
					},
				},{
					title: 'Dimensions',
					render: function(data, i, j, cell) {
						var img = data[i];
						cell.text(img.width+'x'+img.height+'@'+img.resolution);
					},
				},{
					title: 'Type',
					render: function(data, i, j, cell) {
						var img = data[i];
						cell.text(img.type[0]+img.type.slice(1).toLowerCase());
					},
				},{
					title: 'Uploaded on',
					render: function(data, i, j, cell) {
						var date = pgTimestamptzToDate(images[i].createdon).toLocaleString();
						cell.text(date.toString());
					},
				},{
					title: 'Owner',
					render: function(data, i, j, cell) {
						cell.text(images[i].owner);
					},
				},{
					title: 'Usage',
					render: function(data, i, j, cell) {
						assert(i in map);
						cell.text(map[i][1].length);
					},
				},{
					title: 'in exercises',
					render: function(data, i, j, cell) {
						assert(i in map);
						var m = map[i];
						if (m[0].length == 0)
						{
							cell.append($('<span />').text('None').css('font-style','italic'));
						}
						else
						{
							for (var k=0;k<m[0].length;++k) {
								if (k>0) cell.append(', ');
								cell.append($('<a />').attr('href','#exercise/'+m[0][k]).text(m[1][k]));
							};
						}
					},
				},{
					title: '',
					render: function(data, i, j, cell) {
						var image = images[i];
						cell.append($('<img />').attr('src', 'css/icons/backend/Delete16.png').attr('alt','Delete').css('cursor','pointer').click(function() {
							var dlg = $('<div />').append($('<p />').text("Do you really want to delete the image '"+image.name+"' ?"));
							assert(i in map);
							var needsCascade = false;
							if (map[i][0].length > 0)
							{
								dlg.append($('<p />').css('font-weight','bold').text('Warning: all exercises (and results thereof) will be deleted. There is no undo !'));
								needsCascade = true;
							}
							dlg.dialog({
								title: 'Confirm image deletion',
								//width: 310,
								modal: true,
								buttons: {
									'OK': function() {
										udb.app().getModel().rpc('deleteImage', {id:image.id,cascade:needsCascade}, function() {
											cell.parents('tr').remove();
											dlg.dialog('close');
										});
									},
									'Cancel': function() { 
										$(this).dialog('close');
									},
								},
								close: function() {
									$(this).remove();
								},
							});
						}));
					},
				}]
			});
		});
	};
}

