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

// Handy sprintf() replacement. See
// http://stackoverflow.com/questions/610406/javascript-printf-string-format
// Usage: "{0} is much cooler than {1} ?".format("Python", "Javascript")
String.prototype.format = function()
{
    var formatted = this;
    for( var i = 0; i < arguments.length; i++ )
    {
        var regexp = new RegExp( '\\{' + i + '\\}', 'gi' );
        formatted = formatted.replace( regexp, arguments[ i ] );
    }
    return formatted;
};

merge = function( obj1, obj2 )
{
    for( attrname in obj2 )
    {
        obj1[ attrname ] = obj2[ attrname ];
    }
    return obj1;
}

array_shuffle = function( a )
{
    var j = 0;
    var valI = '';
    var valJ = valI;
    var l = a.length - 1;
    while( l > -1 )
    {
        j = Math.floor( Math.random() * l );
        valI = a[ l ];
        valJ = a[ j ];
        a[ l ] = valJ;
        a[ j ] = valI;
        l = l - 1;
    }
    return a;
}

array_chunk = function( input, size )
{
    for( var x, i = 0, c = -1, l = input.length, n = []; i < l; i++ )
    {
        ( x = i % size ) ? n[ c ][ x ] = input[ i ] : n[ ++c ] = [
            input[ i ]
        ];
    }
    
    return n;
}

// Remove all occurrences of an element from an array.
// Hoping that the next function I need to write here is not the '+' operator
// for integer numbers
// EDIT: this, in contrast, already exists in standard Javascript. Pfeww, what a
// relief.
function array_remove( array, value )
{
    var idx = -1;
    do
    {
        idx = array.indexOf( value );
        if( idx >= 0 )
            array.splice( idx, 1 );
    } while( idx >= 0 );
    return array;
}

/** @nosideeffects */
log = function( what )
{
    if( typeof console == 'undefined' )
        return;
    if( console && console.log )
    {
        // console.trace('Logging stuff');
        console.log( what );
    }
}

/** @nosideeffects */
assert = function( cond )
{
    if( !cond )
    {
        if( console && console.trace )
            console.trace( 'Assertion failed' );
        
        throw 'Assertion failed';
    }
}

clampAngle = function( angle )
{
    while( angle > 180.0 )
        angle -= 360.0;
    while( angle < -180.0 )
        angle += 360.0;
    return angle;
}

// jQuery 'plugin' function removeCSS()
// proposed by bluish, on
// http://stackoverflow.com/questions/955030/remove-css-from-a-div-using-jquery
// This function removes a specific value from the style attribute by parsing it
// into an array, removing
// the desired style and rebuilding it.
jQuery.fn.extend( {
    removeCSS: function( cssName )
    {
        
        return this.each( function()
        {
            
            if( !$( this ).attr( 'style' ) )
                return $( this );
            
            return $( this ).attr( 'style',

            jQuery.grep( $( this ).attr( 'style' ).split( ";" ), function( curCssName )
            {
                if( curCssName.toUpperCase().indexOf( cssName.toUpperCase() + ':' ) < 0 )
                    return curCssName;
            } ).join( ";" ) );
        } );
    }
} );

// jQuery 'plugin' function serializeObject()
// proposed by Tobias Cohen, on
// http://stackoverflow.com/questions/1184624/serialize-form-to-json-with-jquery
// Works like $.serializeArray but puts the results in an associative object.
// Handles duplicate (multiple fields
// with the same name name) by aggregating values into an array.
$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each( a, function()
    {
        if( o[ this.name ] !== undefined )
        {
            if( !o[ this.name ].push )
            {
                o[ this.name ] = [
                    o[ this.name ]
                ];
            }
            o[ this.name ].push( this.value || '' );
        } else
        {
            o[ this.name ] = this.value || '';
        }
    } );
    return o;
};

createToolSlider = function( label, sliderConf, showValue )
{
    if( typeof( showValue ) === "undefined" )
        var showValue = true;
    
    var setValueLabel = function( value )
    {
        var v = value;
        if( 'unit' in sliderConf )
            v += ' ' + sliderConf.unit;
        sliderOuter.find( 'span._value' ).text( v );
    };
    
    var conf = {
        min: 1,
        max: 100,
        value: 50,
        slide: function( event, ui )
        {
            if( showValue )
            	setValueLabel( ui.value );
            
            if( sliderConf && 'onSlide' in sliderConf )
                sliderConf.onSlide( ui.value );
        },
        outerClass: 'pianos4_tool_slider',
    };
    $.extend( conf, sliderConf );
    
    var sliderOuter = $( '<div />' ).addClass( conf.outerClass );
    var slider = $( '<div />' );
    slider.slider( conf );
    var value = $( '<span />' );
    if( 'unit' in conf )
        value.text( ' ' + conf.unit );
    sliderOuter.append( $( '<span class="_label" />' ).text( label ) ).append( $( '<span class="_value" />' ) ).append( slider );
    if( showValue )
    	setValueLabel( conf.value );
    return sliderOuter;
}

// Returns true if the specified SVG node has the specified CSS class.
// Firefox provides the handy 'classList' attribute, so this function does
// not make much sense for it, but as other browsers lack it, it fills the gap.
// Note: this function will (voluntarily) fail for regular HTML DOM nodes that
// do not have an SVGAnimatedString as a className (or anything that does
// not have the baseVal thing.
var svgHasClass = function( node, myclass )
{
    if( node.classList )
    {
        return node.classList.contains( myclass );
    } else if( node.className )
    {
        return node.className.baseVal.split( ' ' ).indexOf( myclass ) >= 0;
    } else
        return false;
}

// Function to set the user selection to the provided (pure HTML) DOM element
// Credit to Tim Down
// Source:
// http://stackoverflow.com/questions/4183401/can-you-set-and-or-change-the-users-text-selection-in-javascript
function selectElementContents( el )
{
    if( window.getSelection && document.createRange )
    {
        var sel = window.getSelection();
        var range = document.createRange();
        range.selectNodeContents( el );
        sel.removeAllRanges();
        sel.addRange( range );
    } else if( document.selection && document.body.createTextRange )
    {
        var textRange = document.body.createTextRange();
        textRange.moveToElementText( el );
        textRange.select();
    }
}

// Converts a timestamptz from PostgreSQL into a Javascript Date object
// The JS parser (at least using Mozilla) is broken and does not allow
// ISO-8601 conforming dates like '2012-02-20 09:54:56.34+01' to be interpreted
// correctly. It needs a format like '2012-02-20T09:54:56.34+01:00' (notice the
// date/time separator 'T' and the to-the-minute time zone offset). I hate the
// JS ecosystem - again.
pgTimestamptzToDate = function( iso_time )
{
    // Try to decode the string directly - we never know
    var ts = Date.parse( iso_time );
    if( isNaN( ts ) )
    {
        var input = iso_time.replace( ' ', 'T' );
        var plus_or_minus = input.lastIndexOf( '+' );
        // If the timezone offset is negative, we're looking for a minus sign,
        // not plus
        if( plus_or_minus == -1 )
            plus_or_minus = input.lastIndexOf( '-' );
        var colon = input.lastIndexOf( ':' );
        if( plus_or_minus >= 0 && colon < plus_or_minus )
        {
            input += ':00';
        }
        ts = Date.parse( input );
        if( isNaN( ts ) )
        {
            // Okay, take the hammer out and try again. Not sure if this portion
            // is required/used on something else than IE9.
            // Source:
            // http://stackoverflow.com/questions/5802461/javascript-which-browsers-support-parsing-of-iso-8601-date-string-with-date-parse
            // Not sure if it's correct, though
            var reIsoDate = /^(\d{4})-(\d{2})-(\d{2})((T)(\d{2}):(\d{2})(:(\d{2})(\.\d*)?)?)?(Z$|\+\.+$)?/;
            var m = input.match( reIsoDate );
            if( m )
                return new Date( Date.UTC( +m[ 1 ], +m[ 2 ] - 1, +m[ 3 ], +m[ 6 ] || 0, +m[ 7 ] || 0, +m[ 9 ] || 0, parseInt( ( +m[ 10 ] ) * 1000 ) || 0 ) );
            else
                throw 'Could not parse timestamp as ISO-8601: ' + iso_time;
        }
    }
    return new Date( ts );
}

// Get the parameter named 'name' from the query string of the current URL.
// Source and credits:
// http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery
function getURLParameter( name )
{
    return decodeURIComponent( ( location.search.match( RegExp( "[?|&]" + name + '=(.+?)(&|$)' ) ) || [
        , null
    ] )[ 1 ] );
}
