/*
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/.
*/

/**
 * @constructor
 */
function Pianos4ToolReview(params)
{
	// ------------------------------
	// Constructor
	// ------------------------------
	
	params = params || {};
	if (!params.lineColor) params.lineColor = '#dd3300';
	if (!params.fillColor) params.fillColor = '#101000';
	
	// ------------------------------
	// Public methods
	// ------------------------------
	
	this.getName = function()
	{
		return 'review';
	}
	
	this.getLabel = function()
	{
		return 'Review';
	}
	
	this.getIcon = function()
	{
		return 'icons/select_analysis.png';
	}
	
	this.bind = function(aView)
	{
		assert(!view);
		view = aView;
	}
	
	this.unbind = function()
	{
		assert(view);
		view = null;
	}
	
	this.activate = function()
	{
	}
	
	this.deactivate = function()
	{
	}
	
	this.onMouseDown = function(pos, target, event)
	{
		// if (popup)
			// return false;
			
		assert(!dragSel);
		dragSel = {
			from: pos,
			to: pos,
			quad: view.getSVG().append('svg:rect')
					.attr('x', pos[0])
					.attr('y', pos[1])
					.attr('width', 0)
					.attr('height', 0)
					.style('stroke', params.lineColor)
					.style('stroke-width', 1.0/view.getZoomFactor())
					.style('fill', params.lineColor)
					.style('fill-opacity', 0.3)
		};
	}
	
	this.onMouseMove = function(pos, target)
	{
		// if (popup)
			// return false;
			
		if (dragSel)
		{
			dragSel.to = pos;
			var p = $.extend(true,[],dragSel.from);
			var size = [pos[0]-p[0], pos[1]-p[1]];
			for (var i=0;i<2;++i) {
				if (size[i] < 0.0) {
					p[i] += size[i];
					size[i] = Math.abs(size[i]);
				}
			} 
			dragSel.quad.attr('x', p[0]).attr('y', p[1]).attr('width', size[0]).attr('height', size[1]);
			dragSel.tl = p;
			dragSel.br = [p[0]+size[0],p[1]+size[1]];
		}
	}
	
	this.onMouseUp = function(pos, target, event)
	{
		// if (popup)
			// return false;
			
		if (dragSel)
		{
			dragSel.quad.remove();
			
			if ('tl' in dragSel)
			{
				var sel = [];
				var minutiae = view.getData('minutiae').minutiae;
				for (id in minutiae)
				{
					var min = minutiae[id];
					if (min.pos[0] >= dragSel.tl[0] && min.pos[0] <= dragSel.br[0]
					&& min.pos[1] >= dragSel.tl[1] && min.pos[1] <= dragSel.br[1])
						sel.push(min);
				}
				
				if (sel.length)
				{
					showPopup(sel);
				}
			}
			
			dragSel = null;
		}
		return true;
	}
	
	this.cancel = function()
	{
		if (popup)
			hidePopup();
			
		if (dragSel)
		{
			dragSel.quad.remove();
			dragSel = null;
		}

	}

	this.getOptionsPanel = function()
	{
		if (!options)
		{
			var panel = $('<div />').addClass('pianos4_review_options');
			
			panel.append($('<a />').attr('href','#').text('Export ...').click(function(e) {
				e.preventDefault();
				
				var dlg = $('<div />').text('Please choose the exportation format and options:');
				var id_base = 'pianos4_review_opt_'+(new Date().getTime())
				
				var form = $('<form />').attr('action','?action=export').attr('enctype','multipart/form-data').attr('method','post').appendTo(dlg);
				
				var mkradios = function(title, key, items)
				{
					var div = $('<div />').css('margin','0.5em 0');
					
					var id = id_base + '_' + key;
					var title = $('<span />').text(title).css('display','inline-block').css('width','5em');
					div.append(title);
					
					for (var i in items)
					{
						var item = items[i];
						var label = $('<label />').attr('for',id+'_'+i).text(item.label).css('display','inline-block').css('width','7em');
						var opt = $('<input />').attr('id',id+'_'+i).attr('type','radio').attr('name',key).attr('value',item.value);
						
						if (item.disabled)
						{
							label.css('color','#666666');
							opt.prop('disabled',true);
						}
							
						if (i == 0)
							opt.prop('checked',true);
							
						div.append(opt).append(label);
					}
					
					form.append(div);
				};
				
				var views = pianos.getViews();
				var count = 0;
				var vsel = {};
				for (var i in views)
				{
					var view = views[i];
					var sel = view.getSelection();
					for (var j in sel)
						count++;
					vsel[i] = sel;
				}

				mkradios('Subset', 'subset', [
					{ label: 'All', value: 'all' },
					{ label: 'Users ticked', value: 'users' },
					{ label: 'Selection', value: 'selection', disabled:count==0 },
					{ label: 'Visible', value: 'screen', disabled:true },
				]);
				mkradios('Format', 'format', [
					{ label: 'JSON', value: 'json' },
					{ label: 'CSV', value: 'csv' },
				]);
				mkradios('Delivery', 'delivery', [
					{ label: 'Download', value: 'download' },
					{ label: 'View online', value: 'inline' },
				]);
				
				var downloadData = $('<input />').attr('type','hidden').attr('name','data').appendTo(form);
				var exidData = $('<input />').attr('type','hidden').attr('name','exid').appendTo(form);
				
				var opts = {
					title: 'Export results',
					width: 600,
					modal: true,
					buttons: {
						'OK': function() {
							var settings = form.serializeObject();
							var indata = null;
							var exid = getURLParameter('id');
							if (settings.subset == 'users')
							{
								var vmap = pianos.getUsersResultsVisibility();
								indata = pianos.getData(null, function(annot,view) {
									assert('userid' in annot && annot.userid in vmap);
									return vmap[annot.userid];
								});
							}
							else if (settings.subset == 'selection')
							{
								indata = pianos.getData(null, function(annot, view) {
									var vtype = view.getType();
									if (!(vtype in vsel))
										return false;
									var sel = vsel[vtype];
									return annot.id in sel;
								});
							}
							else if (settings.subset == 'screen')
							{
								assert(false);
							}
							else
							{
								indata = pianos.getData();
							}
							
							var data = {};
							var serverData = pianos.getServerData();
							var serverDataIdx = null;
							if (serverData)
							{
								// serverData is the complete dataset retrieved from the server (or per initialization)
								// It contains the annotations as they were lastly fetched (which may differ from
								// the annotations currently shown/stored in the various controls of the UI) and also
								// an additionnal piece of info we want to export: the observations.
								// It's a mere array of results, and needs to be indexed by user for fast retrieval.
								serverDataIdx = {};
								for (var i in serverData)
								{
									var result = serverData[i];
									if ('observations' in result)
									{
										assert('user' in result && 'id' in result.user);
										serverDataIdx[result.user.id] = i;
									}
								}
							}
							
							for (var vtype in indata)
							{
								var invdata = indata[vtype];
								var vdata = {
									'image': null,
									'users': {}
								};
								for (atype in invdata)
								{
									if (atype == 'image')
									{
										vdata[atype] = invdata[atype];
									}
									else
									{
										var inadata = invdata[atype];
										for (var aid in inadata)
										{
											var annot = inadata[aid];
											var udata = null;
											if (!(annot.userid in vdata['users']))
											{
												udata = {};
												if (serverDataIdx && annot.userid in serverDataIdx)
												{
													assert('observations' in serverData[serverDataIdx[annot.userid]]);
													udata['observations'] = $.extend(true, {}, serverData[serverDataIdx[annot.userid]].observations);
												}
												else
												{
													udata['observations'] = {};
												}
												vdata['users'][annot.userid] = udata;
											}
											else
												udata = vdata['users'][annot.userid];
											
											var tdata = null;
											if (!(atype in udata))
											{
												tdata = {};
												udata[atype] = tdata;
											}
											else
												tdata = udata[atype];
												
											var copy = $.extend(true, {}, annot);
											delete copy.userid;
											tdata[aid] = copy;
										}
									}
								}
								assert(vdata['image']);
								data[vtype] = vdata;
							}
							
							if (settings.delivery == 'download')
							{
								downloadData.val(JSON.stringify(data));
								exidData.val(exid);
								form.submit();
							}
							else
							{
								pianos.ajax({
									pendingMessage: 'Exporting',
									type: 'post',
									processData: false,
									contentType: 'application/json',
									dataType: 'text',
									url: '?action=export',
									data: JSON.stringify($.extend(settings, {
										data: data,
										exid: exid,
									})),
									success: function(data) {
										showExportDialog(data);
									},
									error: function(xhr, status, error) {
										alert('HTTP error (code '+xhr.status+'): '+(error?error:'connection failed'));
									},
								});
							}
							
							$(this).dialog('close');
						},
						'Cancel': function() { $(this).dialog('close'); }
					},
					close: function() { $(this).remove(); },
				};
				
				pianos.dialog(dlg, opts);
				
			}));
			
			options = panel;
		}
		
		return options;
	}
	
	
	// ------------------------------
	// Private variables
	// ------------------------------
	
	var self = this;
	var view = null;
	var dragSel = null;
	var popup = null;
	var options = null;
	
	// ------------------------------
	// Private functions
	// ------------------------------

	var showPopup = function(sel)
	{
		assert(view);
		
		if (!popup)
		{
			popup = $('<div class="pianos4_tool_review_popup" />');

			popup.dialog({
				title: 'Annotations by user',
				autoOpen: false,
				width: 500,
				buttons: {
					'OK': function() {
						hidePopup();
					},
				},
			});
		}
		
		var div = $('<div />');
		var overview = $('<table class="overview"><thead><tr /></thead><tbody></tbody></table>').appendTo(div);
		var details = $('<table class="details"><thead><tr /></thead><tbody></tbody></table>');
		
		
		var rowClasses = ['even','odd'];
		var minutiaTypes = {
			'bifurcation': 'BIF',
			'ridge_ending': 'END',
			'unknown2': 'TUNK',
			'unknown': 'PUNK',
			'difference': 'DIFF',
			'center': 'CENT',
		};
		
		var users = pianos.getUsers();
		var minutiae = {'-1':{}};
		for (var j in minutiaTypes) // We store the sum of all annotations from all users at the -1 index
			minutiae[-1][j] = []; 
		users[-1] = {id:-1,name:'All users'};
		
		details.find('thead tr')
			.append($('<th />').text('User'))
			.append($('<th />').text('ID'))
			.append($('<th />').text('Type'))
			.append($('<th />').text('X'))
			.append($('<th />').text('Y'))
			.append($('<th />').text('Angle'))
			.append($('<th />').text('Pair'));
		
		for (var i in sel)
		{
			var annotation = sel[i];
			if (view.getAnnotationType(annotation.id) == 'minutia')
			{
				assert(annotation.userid in users && annotation.type in minutiaTypes);
				var user = users[annotation.userid];
				
				if (!(user.id in minutiae))
				{
					minutiae[user.id] = {};
					for (var j in minutiaTypes)
						minutiae[user.id][j] = [];
				}
				minutiae[user.id][annotation.type].push(annotation.id);
				minutiae[-1][annotation.type].push(annotation.id);
				
				var tr = $('<tr />').addClass(rowClasses[i%2]);
				tr.append($('<td />').text(user.name));
				tr.append($('<td />').text(user.id));
				tr.append($('<td />').text(minutiaTypes[annotation.type]));
				tr.append($('<td />').text(annotation.pos[0].toFixed(2)));
				tr.append($('<td />').text(annotation.pos[1].toFixed(2)));
				tr.append($('<td />').text(annotation.angle.toFixed(2)));
				tr.append($('<td />').text('pairing' in annotation ? annotation.pairing : ''));
				details.append(tr);
			}
		}
		
		var overview_labels = {'BIF':'Bifurcation','END':'Ridge ending','TUNK':'Type unknown','PUNK':'Position unknown', 'DIFF':'Difference', 'CENT':'Center'};
		var overview_head_tr = overview.find('thead tr');
		overview_head_tr.append($('<th />').text('Author'));
		for (var j in minutiaTypes)
		{
			overview_head_tr.append($('<th class="rotated" />').text(overview_labels[minutiaTypes[j]]));
		}
		
		var currentView = view;
		var overview_body = overview.find('tbody');
		i=0;
		for (var userid in minutiae)
		{
			var user = users[userid];
			var data = minutiae[userid];
			
			var tr = $('<tr />').addClass(rowClasses[i++%2]);
			tr.append($('<td class="username" />').text(user.name));
			for (var j in minutiaTypes) (function(mids)
			{
				var td = $('<td />');
				if (mids.length > 0)
				{
					$('<span />').addClass('nonzero').text(mids.length).data('pianos4_review',mids)
						.bind('mouseenter', function(e) {
							var d = $(this).data('pianos4_review');
							toggleMinutiae(currentView, d, true);
						}).bind('mouseleave', function(e) {
							var d = $(this).data('pianos4_review');
							toggleMinutiae(currentView, d, false);
						}).appendTo(td);
				}
				tr.append(td);
			})(data[j]);
			overview_body.append(tr);
		}
		overview_body.find('tr:first').addClass('allusers');
		
		var hint = $('<div class="details_hint" />')
			.append($('<span />').text('To paste into Excel: '))
			.append($('<a href="#" />').text('select the data').bind('click', function(e) { 
				e.preventDefault();
				//$(this).parent().siblings('table').get(0).select();
				selectElementContents($(this).parent().siblings('table').get(0));
			}))
			.append($('<span />').text(', copy and click Paste > Paste Special > As Unicode Text'));
		
		div.append($('<div />').append('<span class="toggle_details ui-icon ui-inline-icon ui-icon-triangle-1-e" />').append($('<a href="#toggle" class="toggle_details" />').text('Toggle details').bind('click', function(e) {
			e.preventDefault();
			$(this).prev().toggleClass('ui-icon-triangle-1-s');
			$(this).next().toggle();
		})).append($('<div />').append(details).append(hint).hide()));
		
		var content = popup.find('>div');
		content.remove();
		popup.append(div);
		
		popup.css('max-height', Math.max(200,$(window).height()-200)+'px');
		
		popup.dialog('open');
	}
	
	var hidePopup = function()
	{
		assert(popup);
		popup.dialog('close');
		popup.remove();
		popup = null;
	}
	
	var toggleMinutiae = function(currentView, ids, on)
	{
		var layer = currentView.getSVG().select('.pianos4_minutiae');
		var clonables = {};
		for (var i in ids)
		{
			var mid = ids[i];
			if (on)
			{
				// We want to display an overlay over the reviewed minutia. We can't just re-use the gizmo because
				// attributes of a <use> node cannot override those of the referenced node - namely, the color. So
				// we find the node referenced by the gizmo, clone it, modify the relevant attributes and insert it
				// into the SVG. This also looks because unlike jQuery, d3.js does not have a .clone() method.
				var sel = layer.select('#minutia_'+mid+' ._sel');
				var nodeID = sel.attr('xlink:href');
				if (!(nodeID in clonables))
					clonables[nodeID] = currentView.getSVG().select('defs '+nodeID).node();
				var toClone = clonables[nodeID];
				var cloned = toClone.cloneNode(true);
				d3.select(cloned).attr('id',null).classed('_review', true).attr('stroke',params.lineColor).attr('fill',params.lineColor).attr('fill-opacity','0.3');
				sel.node().parentNode.appendChild(cloned);
			}
			else
			{
				var m = layer.select('#minutia_'+mid+' ._review');
				m.remove();
			}
		}
	}
	
	var showExportDialog = function(data)
	{
		var div = $('<div />');
		
		var pre = $('<pre />').appendTo(div);
		
		pre.append(data);
		
		pianos.dialog(div, {
			title: 'Exported data',
			close: function() { $(this).remove(); },
			width: 500,
			height: Math.max(150,$(window).height()*90.0/100.0),
			buttons: {
				'Close': function() { $(this).dialog('close'); }
			},
		});
	}
}
