/*
 * 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 Pianos4ToolTPS( params )
{
    // ------------------------------
    // Constructor
    // ------------------------------
    
    params = params || {};
    if( typeof ( params.v3 ) == 'undefined' )
        params.v3 = false;
    
    // ------------------------------
    // Public methods
    // ------------------------------
    
    this.getName = function()
    {
        return 'TPS';
    }
    
    this.getLabel = function()
    {
        return 'TPS';
    }
    
    this.getIcon = function()
    {
        return 'icons/tool_TPS.png';
    }
    
    this.bind = function( aView )
    {
        assert( !view );
        view = aView;
    }
    
    this.unbind = function()
    {
        assert( view );
        view = null;
    }
    
    this.activate = function()
    {
        views = pianos.getViewsdict();
        
        bindTo( 'mark' );
        bindTo( 'print' );
        
        update_TPS_parameters();
        
        pianos.toggleLayer( 'TPSlines', true );
        pianos.toggleLayer( 'GC', true );
        
        log( 'TPS tool activate' );
    }
    
    this.deactivate = function()
    {
        views.mark.remove_Ghost_Cursor_live();
        views.print.remove_Ghost_Cursor_live();
        
        unbindFrom( 'mark' );
        unbindFrom( 'print' );
        
        pianos.toggleLayer( 'TPSlines', false );
        pianos.toggleLayer( 'GC', false );
        
        removeAllTPSlines();
        
        log( 'TPS tool de-activate' );
    }

    this.onMouseDown = function( pos, target, event )
    {
    }

    this.onMouseUp = function( pos, target, event )
    {
        if( typeof( TPS_g.mark.be ) === "undefined" )
            update_TPS_parameters();
        
        var viewtype = view.getType();
        var clickedView = views[ viewtype ];
        
        var posnotrotated = pos;
        var rotationData = rotationView[ view.getType() ];
        pos = rotateAround( pos, rotationData );
        
        var p = TPS_project( TPS_g[ viewtype ], pos );
        
        clickedAnchor = new Pianos4TPSAnchore( viewtype, "user", pos );
        
        action = new Pianos4Action_CreateAnnotation( clickedView, 'GC', clickedAnchor );
        pianos.exec( action );
        
        updateAllViews();
        
        // Force toggle the GC layer
        pianos.toggleLayer( 'GC', true );
        
        return true;
    }

    this.onMouseMove = function( pos, target, event )
    {
    }

    this.getOptionsPanel = function()
    {
        if( !options )
        {
            var panel = $( '<div class="pianos4_TPS_options" />' );
            
            var generate = $( '<a />' )
                .text( 'Compute' )
                .attr( 'href', '#generate' )
                .bind( 'click', compute_TPS );
            
            var clear = $( '<a />' )
                .text( 'Clear' )
                .attr( 'href', '#clear' )
                .bind( 'click', clearanchors );
            
            panel.append( generate );
            panel.append( $( '<br />' ) );
            panel.append( clear );
            
            if( pianos.getParams().TPS_grid_url !== null )
            {
            	var grid = $( '<a />' )
		            .text( 'Get Grid' )
		            .attr( 'href', '#grid' )
		            .attr( 'id', 'gridlink' )
		            .bind( 'click', TPS_get_grid );
	            
	            panel.append( $( '<br />' ) );
	            panel.append( grid );
            }
            
            options = panel;
        }
        
        return options;
    }

    this.getHelp = function()
    {
        return Pianos4Help.compile();
    }

    // ------------------------------
    // Private variables
    // ------------------------------
    
    var self = this;
    var view = null;
    var views = null;
    var popup = null;
    var options = null;
    var drawTimestamp = Date.now();
    var TPS_g = {};
    
    // ------------------------------
    // Private functions
    // ------------------------------
    
    var bindTo = function( to )
    {
        var d3svg = d3.select('#pianos .pianos4_' + to + ' .pianos4_view_canvas');
        d3svg.on('mousemove', function()
        {
            onMouseMoveLive( d3.event, views[ to ], to );
        });
    }
    
    var unbindFrom = function( from )
    {
        d3.select( '#pianos .pianos4_' + from + ' .pianos4_view_canvas' )
            .on( 'mousemove', null ); 
    }
    
    var onMouseMoveLive = function( event, view, viewtype )
    {
        if( typeof( TPS_g.mark ) !== "undefined" && typeof( TPS_g.mark.be ) !== "undefined" && Date.now() - drawTimestamp > 1000 / 60 )
        {
            var pos = view.getCanvasPos( [ event.clientX, event.clientY ] );
            
            var posnotrotated = pos;
            var rotationData = rotationView[ view.getType() ];
            pos = rotateAround( pos, rotationData );
            
            // var viewtype = view.getType();
            var otherview = viewtype == "print" ? "mark" : "print";
            var p = TPS_project( TPS_g[ viewtype ], pos );
            
            views[ otherview ].update_Ghost_Cursor_live( p );
            views[ viewtype ].remove_Ghost_Cursor_live();
            
            drawTimestamp = Date.now();
        }
    }
    
    var getAllPairedMinutiae = function()
    {
        var mark = pianos.getView( 'mark' );
        var ref = pianos.getView( "print" );
        var mannotations = mark.getAnnotationsDict();
        
        var markpos = Array();
        var refpos = Array();
        
        for( var i in mannotations.minutiae )
        {
            var m = mannotations.minutiae[ i ];
            if( 'pairing' in m )
            {
                markpos.push( m.pos );
                refpos.push( ref.getPairedMinutia( m.pairing ).pos );
            }
        }
        
        return {
            mark: markpos,
            ref: refpos
        };
    }
    
    var check_nb_minutiae = function( pair )
    {
    	if( pair.mark.length < 3 )
        {
    		toastr.error( "At least 3 minutiae have to be paired to calculate the TPS transformation.", "TPS Generate", { timeout: 5000 } )
        	return false;
		
        } else {
        	if( pair.mark.length == 3 )
        		toastr.info( "With only 3 minutiae paired, the distorsion is purly affine (shearing)", "TPS Generate", { timeout: 5000 } )
    		
    		return true;
        }
    }
    
    var compute_TPS = function()
    {
        update_TPS_parameters();
        updateAllViews();
    }
    
    var update_TPS_parameters = function()
    {

        var pair = getAllPairedMinutiae();
        if( check_nb_minutiae( pair ) )
        {
        	TPS_g.mark  = TPS_generate( pair.mark, pair.ref );
        	TPS_g.print = TPS_generate( pair.ref, pair.mark );
        }
        
        show_TPS_lines();
    }
    
    var show_TPS_lines = function()
    {
        var rotate = function( array , times )
        {
            array = array.slice();
            while( times-- ){
                var temp = array.shift();
                array.push( temp )
            }
            return array;
        }
        
        removeAllTPSlines();
        
        var pair = getAllPairedMinutiae();
        
        for( var i in pair.mark )
        {
            var mark = rotate( pair.mark, i );
            var m = mark.shift();
            
            var ref = rotate( pair.ref, i );
            var p = ref.shift();
            
            g = TPS_generate( mark, ref );
            
            var proj = TPS_project( g, m );
            
            var line = new Pianos4TPSLines( views.mark, p, proj );
            views.print.add_TPS_line( line );
        }
        
        for( var i in pair.ref )
        {
            var mark = rotate( pair.mark, i );
            var m = mark.shift();
            
            var ref = rotate( pair.ref, i );
            var p = ref.shift();
            
            g = TPS_generate( ref, mark );
            
            var proj = TPS_project( g, p );
            
            var line = new Pianos4TPSLines( views.print, m, proj );
            views.mark.add_TPS_line( line );
        }
    }
    
    var removeAllTPSlines = function()
    {
        views.mark.removeTPSlines();
        views.print.removeTPSlines();
    }
    
    var TPS_get_grid = function()
    {
    	var pair = getAllPairedMinutiae();
    	
    	if( check_nb_minutiae( pair ) )
    	{
	    	var printimg = pianos.getView( 'print' ).getImage();
	    	
	    	data = {
	        	src: JSON.stringify( pair.ref ),
	        	dst: JSON.stringify( pair.mark ),
	        	maxx: printimg.width,
	        	maxy: printimg.height,
	        	height: printimg.height,
	        	res: printimg.resolution,
	        	unit: 'px',
	    	};
	    	
	    	pianos.ajax( {
	            pendingMessage: 'TPS Grid calculation',
	            type: 'post',
	            url: pianos.getParams().TPS_grid_url,
	            data: data,
	        	dataType: "text",
	        	async: true,
	        	success: function( data ) {
	        		if( data.length <= 0 )
	        		{
	        			toastr.error( 'No image returned', 'TPS Grid error', { timeout: 5000 } );
	        		} else {
		        		var div = $( '<div />' )
		        			.attr( 'align', 'center' );
		        		
		        		pianos.dialog( div, {
		        			title: 'Distorsion grid',
		        			autoOpen: false,
		        			width: 800,
		        			height: 800,
		        			modal: true,
		        			buttons: {
		        				'Close': function() {
		        					$(this).dialog( 'close' );
		        				},
		        			},
		        			close: function()
		        			{
		        			},
		        		});
		        		
		        		var content = $( '<img />' )
		        			.attr( 'src', 'data:image/png;base64,' + data )
		        			.css( 'max-width', '100%' )
		        			.css( 'max-height', '100%' )
		    			
						div.append( content );
						
		        		div.dialog( 'open' );
	    			}
	        	},
	        	error: function( xhr, status, error ){
	        		if( xhr.status == 0 )
	        			var msg = "TPS Grid server (" + pianos.getParams().TPS_grid_url + ") not running. Contact the sysadmin.";
	        		
	        		else if( xhr.status == 400 )
	        			var msg = "Error in the data provided to the TPS Grid server.";
	        		
	        		toastr.error( msg, 'TPS Grid error', { timeout: 5000 } );
	        		$( "#gridlink" )
	        			.css( 'pointer-events', 'none' )
	        			.css( 'color', 'gray' );
	        	}
	    	} );
        }
    }
    
    var updateView = function( from, to, fg )
    {
        to.clear_Ghost_Cursor_Projected();
        
        var anno = from.getData().GC;
        
        for( var key in anno )
        {
            var pos = TPS_project( fg, anno[ key ].pos );
            var projectedAnchor = new Pianos4TPSAnchore( to, "projected", pos );
            to.draw_Ghost_Cursor_Projected( projectedAnchor );
        }
    }
    
    var updateAllViews = function()
    {
        update_TPS_parameters();
        updateView( views.mark, views.print, TPS_g.mark  );
        updateView( views.print, views.mark, TPS_g.print );
    }
    
    var clearanchors = function()
    {
        views.mark.remove_all_GhostCursor_Anchor();
        views.print.remove_all_GhostCursor_Anchor();
        updateAllViews();
    }
}
