(function( $ )
{
    var o = {
        compensateLeft: 0,
        compensateRight: 0,
        steps: 0
    };

    var enabled = false;
    var stepWidth   = 0;
    var initialRatings = {};
    var sliderWidth = 0;
    var currentRatings = {};
    var hasSubmitted = false;
    var hasRated = false;
    var component = null;

    $.fn.toUserRatings = function( options )
    {
        o = $.extend( o, options );

        component = $( '.user-ratings' );

        sliderWidth = parseInt( component.find( '.slider' ).css( 'width' ) ) - ( o.compensateLeft + o.compensateRight );

        if( o.steps )
            stepWidth = sliderWidth / o.steps;

        component.find( '.slider .bar' )
            .each( function()
            {
                var label = $( this ).parent().prev().attr( 'class' ).match( /ident-([a-z0-9]+)/i )[1];;
                var value = parseInt( $( this ).css( 'width' ) );
                initialRatings[ label ] = value;
            });

        enableMouseHoverListener();
        component.find( 'button.submit-ratings' ).click( submitRating );
    }

    function enableEditing( event )
    {
        if( event )
            event.preventDefault();

        if( enabled )
            return;

        enabled = true;

        component
            .find( '.slider' )
                .each( function()
                {
                    var criterion = $( this ).prev().attr( 'class' ).match( /ident-([a-z0-9]+)/i )[1];//text();
                    if( !hasRated )
                        currentRatings[ criterion.toLowerCase() ] = 2.5;
                    var barWidth = $( this ).find( '.bar span' ).width();
                    $( this ).data( 'value', currentRatings[ criterion.toLowerCase() ] );
                    $( this ).children( '.knob' ).remove();
                    $( this ).append( '<a class="knob"></a>' );
                    $( this ).children( '.knob' )
                        .css({
                            left: barWidth
                        })
                        .mousedown( startDrag )
                });
        $( '.user-ratings a.knob, .user-ratings .submit-again, .user-ratings .submit' ).bind( 'mousedown', function()
        {
            $( '#user-ratings-tip' ).hide();
        });

        if( !hasRated )
        {
        $( '#user-ratings-tip' )
            .css({
                position: 'absolute',
                top: 0,
                left: 275
            })
            .show();
        }

        $( document ).mouseup( endDrag )

        component.find( '.submit-form' ).removeClass( 'hidden' );
        component.find( '.submit-again-form' ).addClass( 'hidden' );

        neutralizeValues();
        updateKnobs();
    }

    function disableEditing( event )
    {
        if( !enabled )
            return;

        enabled = false;

        restoreInitialValues();

        component.find( '.submit-form' ).addClass( 'hidden' );

        component
            .find( 'a.knob' )
                .remove();

        $( '#user-ratings-tip' ).hide();

        enabled = false;
    }

    function enableMouseHoverListener()
    {
        $( document ).bind( 'mousemove', onMouseMove );
    }

    function disableMouseHoverListener()
    {
        $( document ).unbind( 'mousemove', onMouseMove );
    }

    function onMouseMove( event )
    {
        var componentOffset    = component.offset();
        var mouseInTopBound    = event.pageY > componentOffset.top;
        var mouseInBottomBound = event.pageY < componentOffset.top + component.height() + 50;
        var mouseInLeftBound   = event.pageX > componentOffset.left;
        var mouseInRightBound  = event.pageX < componentOffset.left + component.width();

        if( mouseInTopBound && mouseInBottomBound && mouseInLeftBound && mouseInRightBound )
        {
            enableEditing();
        }
        else
        {
            disableEditing();
        }
    }

    function submitRating( event )
    {
        event.preventDefault();

        var data = { uid: component.find( 'input[type="hidden"][name="uid"]' ).val() };
        for( var criterion in currentRatings )
        {
            data[ '_' + criterion ] = currentRatings[ criterion ];
        }

        $.post( '/london/user_rating/', data, onSubmitRating, 'json');

        disableMouseHoverListener();
        disableEditing();
    }

    function onSubmitRating( data )
    {
        component.find( 'button.submit-again' ).bind( 'click', editAgain );

        var numRatings = component.find( '.num-ratings' );
        var newText = numRatings.text().replace( /[0-9]+/, data.numRatings );
        numRatings.text( newText );
        for( var criterion in initialRatings )
        {
            initialRatings[ criterion ] = ( data.ratings[ criterion.toLowerCase() ] / 5 ) * sliderWidth;
        }

        component.find( '.submit-again-form' ).removeClass( 'hidden' );
        $( 'thanks' ).removeClass( 'hidden' );
        restoreInitialValues();
    }

    function editAgain( event )
    {
        event.preventDefault();
        enableMouseHoverListener();
        enableEditing();
    }

    function addRating( event )
    {
        event.preventDefault();
        component
            .find( '.big-button-wrapper, .no-ratings-message' )
                .addClass( 'hidden' )
                .end()
            .find( '.user-ratings-ui' )
                .removeClass( 'hidden' );
        enableEditing();
    }

    function updateKnobs()
    {
        component.find( '.slider .bar' ).each( function()
        {
            $( this )
                .siblings( '.knob' )
                    .css({ left: $( this ).width() });
        });
    }

    function neutralizeValues()
    {
        component.find( '.slider .bar' )
            .each( function()
            {
                var label = $( this ).parent().prev().attr( 'class' ).match( /ident-([a-z0-9]+)/i )[1];
                var value = currentRatings[ label ];
                var scale = o.steps / 2;
                var width = ( value / scale ) * sliderWidth;

                $( this ).animate( {width: width }, {
                    duration: 200,
                    easing: 'swing',
                    step: updateKnobs,
                    complete: updateKnobs
                });
            });
    }

    function restoreInitialValues()
    {
        component.find( '.slider .bar' )
            .each( function()
            {
                var label = $( this ).parent().prev().attr( 'class' ).match( /ident-([a-z0-9]+)/i )[1];
                var value = initialRatings[ label ];

                $( this ).animate( {width: value }, {
                    duration: 200,
                    easing: 'swing',
                    step: updateKnobs,
                    complete: updateKnobs
                });
            });
    }

    function startDrag( event )
    {
        event.preventDefault();

        var target = $( event.currentTarget );
        var mouseCoords = { x: event.pageX, y: event.pageY };
        var targetLeft = target.css( 'left' ).replace( /px$/, '' );

        $( document )
            .data( 'userRatingStartXY', mouseCoords )
            .data( 'userRatingDragTarget', target )
            .data( 'userRatingInitialLeft', targetLeft )
            .mousemove( onDrag );
    }

    function endDrag( event )
    {
        var target = $( document ).data( 'userRatingDragTarget' );
        $( document )
            .data( 'userRatingStartXY',     null )
            .data( 'userRatingDragTarget' , null )
            .data( 'userRatingInitialLeft', null )
            .unbind( 'mousemove', onDrag );
        hasRated = true;
    }

    function onDrag( event )
    {
        var target             = $( document ).data( 'userRatingDragTarget' )
        var initialMouseCoords = $( document ).data( 'userRatingStartXY' );
        var initialTargetLeft  = parseInt( $( document ).data( 'userRatingInitialLeft' ) );

        var diffX = event.pageX - initialMouseCoords.x;
        var mouseX = initialTargetLeft + diffX;

        if( !target.parent().css( 'width' ) )
            return;

        var barWidth = target.parent().css( 'width' ).replace( /px$/, '' );

        if( mouseX < o.compensateLeft )
            mouseX = o.compensateLeft;
        else if( mouseX > barWidth - o.compensateRight  )
            mouseX = barWidth - o.compensateRight;

        var width = ( barWidth - o.compensateRight ) - o.compensateLeft;
        var value = mouseX / width;

        if( o.steps )
        {
            var snapped = false;
            var i = 0;
            while( !snapped && i <= o.steps)
            {
                var step = ( i * stepWidth );
                var halfStepAhead = step + ( stepWidth / 2 );
                if( mouseX < halfStepAhead ){
                    mouseX = step; 
                    snapped = true;
                    continue;
                }
                i++;
            }
            value = i;
        }

        //var criterion = target.parents( '.slider' ).prev().text();
        var criterion = target.parents( '.slider' ).prev().attr( 'class' ).match( /ident-([a-z0-9]+)/i )[1];;
        currentRatings[ criterion.toLowerCase() ] = value / 2;

        target
            .css({
                left: mouseX
            })
            .siblings( '.bar' )
                .css({
                    width: mouseX
                });

        if( mouseX <= 0 )
            target.siblings( '.bar' ).css( 'overflow', 'hidden' );
        else
            target.siblings( '.bar' ).css( 'overflow', 'visible' );
    }
}
)( jQuery );

$( function()
{
    $( '.user-ratings' ).toUserRatings({
        compensateRight: 10,
        steps: 10
    });
});
