/*
 jQuery UI Sortable plugin wrapper

 @param [ui-sortable] {object} Options to pass to $.fn.sortable() merged onto ui.config
*/
angular.module('ui.directives').directive('uiSortable', [
  'ui.config', function(uiConfig) {
    return {
      require: '?ngModel',
      link: function(scope, element, attrs, ngModel) {
        var onReceive, onRemove, onStart, onUpdate, opts, _receive, _remove, _start, _update;

        opts = angular.extend({}, uiConfig.sortable, scope.$eval(attrs.uiSortable));

        if (ngModel) {

          ngModel.$render = function() {
            element.sortable( "refresh" );
          };

          onStart = function(e, ui) {
            // Save position of dragged item
            ui.item.sortable = { index: ui.item.index() };
          };

          onUpdate = function(e, ui) {
            // For some reason the reference to ngModel in stop() is wrong
            ui.item.sortable.resort = ngModel;
          };

          onReceive = function(e, ui) {
            ui.item.sortable.relocate = true;
            // added item to array into correct position and set up flag
            ngModel.$modelValue.splice(ui.item.index(), 0, ui.item.sortable.moved);
          };

          onRemove = function(e, ui) {
            // copy data into item
            if (ngModel.$modelValue.length === 1) {
              ui.item.sortable.moved = ngModel.$modelValue.splice(0, 1)[0];
            } else {
              ui.item.sortable.moved =  ngModel.$modelValue.splice(ui.item.sortable.index, 1)[0];
            }
          };

          onStop = function(e, ui) {
            // digest all prepared changes
            if (ui.item.sortable.resort && !ui.item.sortable.relocate) {

              // Fetch saved and current position of dropped element
              var end, start;
              start = ui.item.sortable.index;
              end = ui.item.index();
              if (start < end)
                end--;

              // Reorder array and apply change to scope
              ui.item.sortable.resort.$modelValue.splice(end, 0, ui.item.sortable.resort.$modelValue.splice(start, 1)[0]);

            }
            if (ui.item.sortable.resort || ui.item.sortable.relocate) {
              scope.$apply();
            }
          };

          // If user provided 'start' callback compose it with onStart function
          _start = opts.start;
          opts.start = function(e, ui) {
            onStart(e, ui);
            if (typeof _start === "function")
              _start(e, ui);
          };

          // If user provided 'start' callback compose it with onStart function
          _stop = opts.stop;
          opts.stop = function(e, ui) {
            onStop(e, ui);
            if (typeof _stop === "function")
              _stop(e, ui);
          };

          // If user provided 'update' callback compose it with onUpdate function
          _update = opts.update;
          opts.update = function(e, ui) {
            onUpdate(e, ui);
            if (typeof _update === "function")
              _update(e, ui);
          };

          // If user provided 'receive' callback compose it with onReceive function
          _receive = opts.receive;
          opts.receive = function(e, ui) {
            onReceive(e, ui);
            if (typeof _receive === "function")
              _receive(e, ui);
          };

          // If user provided 'remove' callback compose it with onRemove function
          _remove = opts.remove;
          opts.remove = function(e, ui) {
            onRemove(e, ui);
            if (typeof _remove === "function")
              _remove(e, ui);
          };
        }

        // Create sortable
        element.sortable(opts);
      }
    };
  }
]);
