/*
 * PiAnoS (Picture Annotation System), A fingerprint examiner training tool
 * Copyright (C) 2011-2021 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 Pianos4ToolMinutia(params)
{
    // ------------------------------
    // Constructor
    // ------------------------------
    
    params = params || {};
    if (!params.lineColor) params.lineColor = '#ff0000';
    if (typeof(params.v3) == 'undefined') params.v3 = false;
    if (typeof(params.autoPairing) == 'undefined') params.autoPairing = true;
    if (!params.phase) params.phase = 'comparison';
    if (typeof(params.singleClickDeselects) == 'undefined') params.singleClickDeselects = false;
    if (typeof params.markCanHaveCenter == 'undefined') params.markCanHaveCenter = false;
    
    // ------------------------------
    // Public methods
    // ------------------------------
    
    this.getName = function()
    {
        return 'minutia';
    }
    
    this.getLabel = function()
    {
        return 'Minutia';
    }
    
    this.getIcon = function()
    {
        return 'icons/tool_minutia.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)
    {
        initpos = pos;
        
        var rotationData = rotationView[ view.getType() ];
        pos = rotateAround( pos, rotationData );
        
        initposrotated = rotateAround( initpos, rotationData );
        
        if (!popup)
        {
            var liveminu = new Pianos4Minutia("live", pos, 0);
            var action = new Pianos4Action_CreateAnnotation(view, 'minutia', liveminu);
            pianos.exec( action );
            
            var t = view.getSVG()
                .select( "#liveminutia" )
                .attr( 'transform', 'translate( ' + pos[ 0 ] + ', ' + pos[ 1 ] + ' ) scale( ' + markers.factor + ' )' );
            
            if (autoPair && !autoPairInfo)
                autoPairInfo = {view:view,first:null};
                
            pianos.toggleLayer('minutiae', true);
        }
    }
    
    this.onMouseMove = function(pos, obj, event)
    {
        var posnotrotated = pos;
        var rotationData = rotationView[ view.getType() ];
        pos = rotateAround( pos, rotationData );
        
        if (!popup)
        {
            var dx = pos[ 0 ] - initposrotated[ 0 ];
            var dy = pos[ 1 ] - initposrotated[ 1 ];
            var d = Math.sqrt( dx * dx + dy * dy );
            
            if( d < 0.01 )
                return;
            
            var vn = [ dx / d, dy / d ];
            var newx = initpos[ 0 ] + vn[ 0 ] * markers.lq2 * markers.factor;
            var newy = initpos[ 1 ] + vn[ 1 ] * markers.lq2 * markers.factor;
            
            var angle = Math.acos( vn[ 0 ] );
            if ( vn[ 1 ] < 0 )
                angle = -angle;
            var degrees = 180.0 * angle / Math.PI;
            var t = view.getSVG()
                .select( "#liveminutia" )
                .attr( 'transform', 'translate( ' + initposrotated[ 0 ] + ', ' + initposrotated[ 1 ] + ' ) rotate( ' + degrees + ' ) scale( ' + markers.factor + ' )' );
            
            endpos = posnotrotated;
            
            if( event.buttons == 0 )
                hideLiveMinutia();
        }
    }
    
    this.onMouseUp = function(pos, target, event)
    {
        var pos0 = initpos;
        var pos1 = endpos;
        
        if( pos0 === null || pos1 === null )
        {
            hideLiveMinutia();
            return true;
        }
        else
        {
            var rotationData = rotationView[ view.getType() ];
            
            var pos0 = rotateAround( pos0, rotationData );
            var pos1 = rotateAround( pos1, rotationData );
            
            var v = [ pos1[ 0 ] - pos0[ 0 ], pos1[ 1] - pos0[ 1 ] ];
            var n = Math.sqrt( v[ 0 ] * v[ 0 ] + v[ 1 ] * v[ 1 ] );
            
            var vn = [ v[ 0 ] / n, v[ 1 ] / n ];
            var angle = Math.acos( vn[ 0 ] );
            if( vn[ 1 ] < 0 )
                angle = -angle;
            var degrees = 180.0 * angle / Math.PI;
            
            /*
             *    Do not annotate the minutiae if the drag length is less than the
             *    radius of the minutiae (see the 'pianos4_view.js' file to see the
             *    AFIS specifications). 
             */
            if( n < ( 0.5 * view.getImageResolution() / ( 25.4 * 2 ) ) )
            {
                hideLiveMinutia();
                return true;
            }
            else if( autoMinutiaeType )
            {
                hideLiveMinutia();

                var minutia = new Pianos4Minutia( "ridge_ending", pos0, degrees );
                var action = new Pianos4Action_CreateAnnotation( view, 'minutia', minutia );
                pianos.exec( action );

                return true;
            }
            else
            {
                if (popup)
                {
                    hideLiveMinutia();
                    hidePopup();
                    
                    if (popup.choice)
                    {
                        var minutia = new Pianos4Minutia(popup.choice, pos0, degrees);
                        minutia.live = false;
                        var action;
                        
                        assert(!autoPair || autoPairInfo);
                        if (autoPair && autoPairInfo.view.getType() != view.getType())
                        {
                            // Auto-pairing is ON and this minutia was created in
                            // another view than the
                            // previous one: pair them together
                            assert(autoPairInfo.first != null);
                            
                            // Use a batch action that will both create the minutia and
                            // do the pairing
                            action = new Pianos4Action_Batch();
                            action.append(new Pianos4Action_CreateAnnotation(view, 'minutia', minutia));
                            var markMin, printMin;
                            if (view.isPrint())
                            {
                                markMin = autoPairInfo.first;
                                printMin = minutia;
                            }
                            else
                            {
                                markMin = minutia;
                                printMin = autoPairInfo.first;
                            }
                            action.append(new Pianos4Action_PairMinutiae(markMin, printMin, Pianos4Annotation._s_pairIds++));
                            
                            // Reset pairing for the next use.
                            autoPairInfo = null;
                        }
                        else
                        {
                            action = new Pianos4Action_CreateAnnotation(view, 'minutia', minutia);
                            
                            if (autoPair)
                            {
                                // Auto-pairing is ON but either:
                                // - this is the first minutia created -> memorize it for the future pairing
                                // - this minutia was created in the same view than the previous one -> memorize it as well, so that pairing will now occur between this minutia and the next one.
                                autoPairInfo.first = minutia;
                            }
                        }
                        
                        pianos.exec(action);
                    }
        
                    if (event.button == 2)
                        pianos.skipNextContextMenu();
        
                    popup = null;
                    return true;
                }
                else
                {
                    // Check if the user has made a click or a click-drag-release, using a fixed tolerance (so that minimal drags are considered as clicks)
                    var p0 = view.getAbsPos(pos0);
                    var p1 = view.getAbsPos(pos1);
                    var v = [p1[0]-p0[0], p1[1]-p0[1]];
                    var n = Math.sqrt(v[0]*v[0]+v[1]*v[1]);
                    if (n < 2)
                    {
                        assert(line && !popup);
                        if (params.singleClickDeselects)
                            view.selectAnnotation([], true);
                        return true;
                    }
                    else
                    {
                        // OK, it was a real drag: show the minutia creation popup
                        var docPos = view.getAbsPos(pos);
                        showPopup(docPos);
                    }
                }
            }
        }
    }
    
    this.cancel = function()
    {
        if (popup)
            hidePopup();
        popup = null;
    }
    
    this.getOptionsPanel = function()
    {
        if (!options)
        {
            var panel = $('<div class="pianos4_minutia_options" />');
        
            // Auto-pairing
            if (params.autoPairing)
            {
                var randomID = 'pianos4_rid_'+(new Date().getTime());
                var autoPairBox = $('<input type="checkbox" '+(autoPair?'checked="checked"':'')+' id="'+randomID+'" />');
                autoPairBox.bind('click', function() { 
                    autoPair = this.checked; 
                    pianos.toggleLayer('pairing', autoPair);
                });
                var label = $('<label for="'+randomID+'" />').text('Automatic pairing');
                
                panel.append($('<div />').append(autoPairBox).append(label));
            }
            
            // Auto minutiae type -- Fast annotation
            var autoMinType = $( '<input type="checkbox" id="pianos4_rid_automintype" />' );
            autoMinType.bind( "click", function(){
                autoMinutiaeType = this.checked;
            } );
            var label = $( '<label for="pianos4_rid_automintype" />' ).text( "Fast annotation" );

            panel.append(
                $( "<div />" )
                    .append( autoMinType )
                    .append( label )
            );
            
            options = panel;
        }
        
        return options;
    }
    
    this.getHelp = function()
    {
        return Pianos4Help.compile([
            'Use the minutia tool to draw minutiae. To place a minutia, click the desired position and, keeping the button depressed, drag along the direction (a red line appears). Release when the direction is OK - the length of the drag does not matter. Finally, choose the appropriate minutia type from the popup menu.',
            'After a minutia has been placed, it can be moved and rotated using the edit tool - or by simply clicking it with this tool. When a minutia is selected (green overlay) the mouse cursor turns into a cross if inside the overlay, and into a rotation symbol if nearby. Click and drag to perform translations and rotations, respectively.',
            'Use the context menu to change the type of the minutia.',
            'Use the \'Marker size\' slider to globally adjust the size of the minutiae - this will not affect your results and is a display-only setting.',
            'The fast annotation option allows you to not set the minutia type with the popup menu; it will be set automatically to ridge ending',
            '<<mouse:left>>: start drawing',
            '<<mouse:right>>: (while drawing) cancel',
            '<<mouse:left>>: (on a minutia) select and move/rotate the minutia',
            '<<mouse:right>>: (on a minutia) pop up the context menu',
            '<<key:shift>><<mouse:left>>: (hold shift) switch to the edit tool to manipulate the minutia',
            '<<key:M>>: (shortcut) switch to the minutia tool',
            '<<key:Ctrl>>: (hold Control) Show the distortion indicators and the ghost cursor; only available in the comparison phase',
            'Note: in PiAnoS, bifurcations point inwards the ridge valley. Ridge endings point up and along the ridge.',
        ]);
    }

    // ------------------------------
    // Private variables
    // ------------------------------
    
    var self = this;
    var view = null;
    var popup = null;
    var options = null;
    var autoPair = false;
    var autoPairInfo = null;
    var markerSize = 4;
    var initpos = null;
    var endpos = null;
    var autoMinutiaeType = false;

    // ------------------------------
    // Private functions
    // ------------------------------
    
    var hideLiveMinutia = function()
    {
        view.getSVG()
            .select( "#liveminutia" )
            .remove();
    }
    
    var hidePopup = function()
    {
        popup.menu.remove();
        popup.overlay.remove();
        pianos.leaveModal();
    }
    
    var showPopup = function(docPos)
    {
        popup = {
            overlay: $('<div class="ui-widget-overlay" style="width:'+$(document).width()+'px;height:'+$(document).height()+'px;z-index:1000;" />'),
            menu: $('<div class="ui-widget ui-widget-content pianos4_canvas_popup" style="z-index:1001;"></div>'),
            choice: null,
        };
        
        var items;
        var labels;
        if (params.v3)
        {
            items = ['bifurcation','ridge_ending','unknown'];
            labels = ['Bifurcation','Ridge ending','Unknown'];
        }
        else
        {
            items = ['bifurcation','ridge_ending','unknown','unknown2'];
            labels = ['Bifurcation','Ridge ending','Type unknown','Position unknown'];
            if (params.phase == 'comparison')
            {
                items.push('difference');
                labels.push('Difference');
            }
        }

        if (params.markCanHaveCenter || view.isPrint())
        {
            items.push('center');
            labels.push('Center');
        }
        
        var ul = $('<ul />');
        for (var i in items)
        {
            var li = $('<li title="'+items[i]+'" class="ui-state-default" />')
                .text(labels[i])
                .bind('mouseenter', function() { $(this).removeClass('ui-state-default').addClass('ui-state-active'); assert(!popup.choice); popup.choice = this.title; })
                .bind('mouseleave', function() { $(this).removeClass('ui-state-active').addClass('ui-state-default'); assert(popup.choice); popup.choice = null; })
            ;
            ul.append(li);
        }

        popup.menu.append(ul);
        popup.menu.css('visibility','none');

        $('body')
            .append(popup.overlay)
            .append(popup.menu);
        
        var docSize = [$(window).width(), $(window).height()];
        var menuPos = [
            Math.max(0, Math.min(docSize[0]-popup.menu.outerWidth(), docPos[0])),
            Math.max(0, Math.min(docSize[1]-popup.menu.outerHeight(), docPos[1]))
        ];

        popup.menu.css('left', menuPos[0]+'px')
            .css('top', menuPos[1]+'px')
            .removeCSS('visibility');

        pianos.enterModal();
    }
}
