/*!
 * jquery.drag-n-crop
 * https://github.com/lukaszfiszer/drag-n-crop
 *
 * Copyright (c) 2013 Lukasz Fiszer
 * Licensed under the MIT license.
 */

;(function ( $, window, document, undefined ) {
  $.widget( "lukaszfiszer.dragncrop" , {

      classes: {
        // Basic classes
        container: 'dragncrop',
        containerActive: 'dragncrop-dragging',
        containment: 'dragncrop-containment',
        horizontal: 'dragncrop-horizontal',
        vertical: 'dragncrop-vertical',

        // Options' classes
        overflow: 'dragncrop-overflow',
        overlay: 'dragncrop-overlay',
        instruction: 'dragncrop-instruction',
        instructionHide: 'dragncrop-instruction-autohide',
        instructionText: 'dragncrop-instruction-text'
      },

      options: {
        // Initial position
        position: {},
        centered: false,

        // Simple overflow:
        overflow: false,

        // Overflaid overflow
        overlay: false,

        // Drag instruction
        instruction: false,
        instructionText: 'Drag to crop',
        instructionHideOnHover: true,
      },

      move: function ( position ) {

        var left, top, x, y;

        if( !position ){
          throw new Error('position object must be provided');
        }

        if (position.offset === undefined && position.dimension === undefined ) {
          throw new Error('position object must contain "left" or "top" props');
        }

        if( this.axis === 'x' && position.offset ){
          left = -position.offset[0] * this.offsetX;
          this.element.css('left', left);
          this.element.css('top', 0);
        } else
        if( this.axis === 'x' && position.dimension ){
          left = -position.dimension[0] * this.width;
          this.element.css('left', left);
          this.element.css('top', 0);
        } else

        if( this.axis === 'y' && position.offset ){
          top = -position.offset[1] * this.offsetY;
          this.element.css('left', 0);
          this.element.css('top', top);
        } else
        if( this.axis === 'y' && position.dimension ){
          top = -position.dimension[1] * this.height;
          this.element.css('left', 0);
          this.element.css('top', top);
        }

        this._setPosition( { left: left, top: top });

      }, 

      _create: function () {

        this.container = $(this.element.parent());
        this.container.addClass(this.classes.container);

        if( this.options.overflow || this.options.overlay){
          $(this.container).addClass(this.classes.overflow);
        }

        var dfd = this.element.imagesLoaded();
        var self = this;

        dfd.done(function(){
          if(self._setAxis.call(self)){
            self._getDimensions.call(self);
            self._makeDraggable.call(self);
            if (self.options.loaded) {
              self.options.loaded();
            }
          }
        } );

      },

      _destroy: function() {
        this.draggable && this.draggable.draggable('destroy');
        this.container.find('.' + this.classes.containment + ',' +
                            '.' + this.classes.overlay  + ',' +
                            '.' + this.classes.instruction).remove();
        this.element.removeClass(this.classes.horizontal)
                    .removeClass(this.classes.vertical);
      },

      getPosition: function() {
        return {
          offset : [
            ( -this.position.left / this.offsetX) || 0,
            ( -this.position.top / this.offsetY) || 0
          ],
          dimension : [
            ( -this.position.left / this.width) || 0,
            ( -this.position.top / this.height) || 0
          ]
        };

      },

      _getDimensions: function() {

        this.width = this.element.width();
        this.height = this.element.height();

        this.containerWidth = this.container.width();
        this.containerHeight = this.container.height();

        this.offsetX = this.width - this.containerWidth;
        this.offsetY = this.height - this.containerHeight;

      },

      _setAxis: function() {

        this.photoRatio = this.element.width() / this.element.height();
        this.containerRatio = this.container.width() / this.container.height();

        if (this.photoRatio > this.containerRatio) {

          this.axis = 'x';
          $(this.element).addClass(this.classes.horizontal);
          return true;

        } else if (this.photoRatio < this.containerRatio) {

          this.axis = 'y';
          $(this.element).addClass(this.classes.vertical);
          return true;

        }else{

          return false;

        }

      },

      _setPosition: function( obj ) {
        this.position = obj;
      },

      _makeDraggable : function () {

        var axis         = this.axis;
        var position     = this.options.position;
        var containement = this._insertContainment();

        var draggable = this.draggable = this.element.draggable({
          axis: axis,
          containment: containement
        });

        this._on({
          dragstart: function (event, ui) {
            this._dragStart( event , ui );
            this.container.addClass( this.classes.containerActive );
          },
          drag: function( event, ui ){
            this._dragging( event , ui );
            if (this.options.overlay) {
              this._adaptOverlay( ui );
            }
          },
          dragstop: function( event, ui ){
            this._dragStop( event , ui );
            this.container.removeClass( this.classes.containerActive );
          }
        });

        if(this.options.overlay){
          this._insertOverlay();
        }

        if(this.options.instruction){
          this._insertInstruction();
        }

        if(this.options.centered){
          position = { offset : [0.5, 0.5] };
        }

        if( position && ( position.offset || position.coordinates)) {
          this.move(position);
        }else{
          this._setPosition({ left: 0, top: 0 });
        }

      },

      _dragStart: function( event, ui ) {
        this._setPosition(ui.position);
        this._trigger('start', event, this.getPosition() );
      },

      _dragging: function( event, ui ) {
        this._setPosition(ui.position);
        this._trigger('drag', event, this.getPosition() );
      },

      _dragStop: function( event, ui ) {
        this._setPosition(ui.position);
        this._trigger('stop', event, this.getPosition() );
      },


      _insertOverlay: function(){

        var overlay = $('<div>').addClass(this.classes.overlay);
        this.overlay = overlay.insertBefore(this.element);
        return this.overlay;

      },

      _adaptOverlay: function( ui ) {

        if ( this.axis === 'x' ) {

          this.overlay.css('left', ui.position.left)
                      .css('border-left-width', -ui.position.left)
                      .css('right', -ui.position.left - this.offsetX)
                      .css('border-right-width', ui.position.left + this.offsetX);

        } else if ( this.axis === 'y' ){

          this.overlay.css('top', ui.position.top)
                      .css('border-top-width', -ui.position.top)
                      .css('bottom', -ui.position.top - this.offsetY )
                      .css('border-bottom-width', ui.position.top + this.offsetY );
        }

      },

      _insertContainment: function() {

        var top    = - this.offsetY;
        var bottom = - this.offsetY;
        var left   = - this.offsetX;
        var right  = - this.offsetX;

        var containment = $('<div/>').addClass(this.classes.containment)
                          .css('top',top).css('bottom', bottom)
                          .css('left',left).css('right',right)
                          .css('position','absolute');

        this.containment = containment.insertBefore(this.element);
        return this.containment;

      },

      _insertInstruction: function() {
        this.instruction = $('<div>').addClass(this.classes.instruction);
        if(this.options.instructionHideOnHover){
          this.instruction.addClass(this.classes.instructionHide);
        }
        this.instruction.append(
          $('<span></span>').text(this.options.instructionText)
                            .addClass(this.classes.instructionText)
        );
        this.instruction.insertAfter(this.element);
        return this.instruction;
      },

  });

})( scriptJquery, window, document );
