(function ($) {

  var methods = {
    /**
    * Initialize flash uploader
    *
    * @param string wrapper_id
    * @return null
    */
    init : function(options) {
      return this.each(function () {
        var wrapper = $(this);
        var wrapper_dom = this;

        wrapper.addClass('advanced_upload');

        this.fu_variables = {};
        this.fu_variables.wrapper_id = wrapper.attr('id');
        this.fu_variables.form = wrapper.parents('form:first');
        this.fu_variables.attachment_list = wrapper.find('.select_attachments_list:first');

        // pick up init options
        this.fu_variables.form_field_name = options.pending_field_name;
        this.fu_variables.form_delete_field_name = options.delete_field_name;

        // construct the uploader
        this.fu_variables.uploader = new plupload.Uploader({
          'runtimes'              : options.uploader_runtimes,
          'container'             : options.wrapper_id + '_attach_file_button_wrapper',
          'browse_button'         : options.wrapper_id + '_attach_file_button',
          'max_file_size'         : options.max_file_size,
          'url'                   : App.extendUrl(options.upload_url, { 'advanced_upload' : 1 }),
          'file_data_name'        : options.upload_name,
          'flash_swf_url'         : options.flash_uploader_url,
          'silverlight_xap_url'   : options.silverlight_uploader_url,
          'multipart'             : true,
          'multipart_params'      : { 'submitted' : 'submitted' }
        });

        // what happens when files are added
        this.fu_variables.uploader.bind('FilesAdded', function (uploader, files) {
          files_added.apply(wrapper_dom, [files]);
        });

        // on file upload progress
        this.fu_variables.uploader.bind('UploadProgress', function(uploader, file) {
          update_file_progress.apply(wrapper_dom, [file]);
        });

        // on file upload completed
        this.fu_variables.uploader.bind('FileUploaded', function (uploader, file, response) {
          eval('var response = ' + response.response);

          if (response && response.type == 'Error') {
            response.file = file;
            upload_error.apply(wrapper_dom, [response]);
          } else {
            finalize_file_upload.apply(wrapper_dom, [file, response]);
          } // if
        });

        // on error
        this.fu_variables.uploader.bind('Error', function (uploader, error) {
          upload_error.apply(wrapper_dom, [error]);
        });

        // when all uploads are completed
        this.fu_variables.uploader.bind('UploadComplete', function (uploader) {
          wrapper_dom.fu_variables.form.removeClass('uploading');
        });

        // initialize uploader
        this.fu_variables.uploader.init();

        // what happens when remove attachment is clicked
        wrapper.on('click', 'td.delete_attachment a', function () {
          remove_file.apply(wrapper_dom, [$(this).parents('tr:first')]);
          return false;
        });

        // insert existing attachments
        if (options.existing_attachments && options.existing_attachments.length) {
          $.each(options.existing_attachments, function (index, existing_attachment) {
            add_file.apply(wrapper_dom, [{ 'name' : existing_attachment.filename }, existing_attachment.id ]);
          });
        } // if
      });
    },

    /**
     * Reset this widget
     */
    'reset' : function () {
      return this.each(function () {
        reset.apply(this);
      });
    }
  };

  /**
   * Fires when files are added
   *
   * @param Array files
   */
  var files_added = function (files) {
    var wrapper_dom = this;

    $.each(files, function (file_id, file) {
      add_file.apply(wrapper_dom, [file]);
    });

    if (this.fu_variables.uploader.state == plupload.STOPPED) {
      setTimeout(function () {
        wrapper_dom.fu_variables.uploader.start();
      }, 20);
    } // if

    this.fu_variables.form.addClass('uploading');
  }; // files_added

  /**
   * Add File to the list
   *
   * @param Object file
   */
  var add_file = function (file, real_id) {
    var attachment_row = $('<tr></tr>').appendTo(this.fu_variables.attachment_list);
    attachment_row.append('<td class="file_icon"><img src="' + get_icon(file.name.clean()) + '" /></td>');
    attachment_row.append('<td class="filename"><div>' + App.excerpt(file.name.clean(), 24) + '</div></td>');
    attachment_row.append('<td class="delete_attachment"><a href="#">' + App.lang('Remove') + '</a></td>');

    if (real_id) {
      attachment_row.attr('real_id', real_id);
    } else {
      attachment_row.attr('uploading_id', file.id).addClass('uploading');
      attachment_row.find('td.filename div').append('<span class="progressbar"><span class="progressbar_inner"></span></span>');
    } // if

  }; // add_file

  /**
   * get icon for file_name
   *
   * @param String file_name
   * @return String
   */
  var get_icon = function (file_name) {
    var dot_index = file_name.lastIndexOf('.');
    if (dot_index >= 0) {
      return App.Wireframe.Utils.imageUrl('/file-types/16x16/' + file_name.substring((dot_index+1)).toLowerCase() + '.png', 'environment');
    } else {
      return App.Wireframe.Utils.imageUrl('/file-types/16x16/default.png', 'environment');
    } // if
  };

  /**
   * Remove file from the list (and upload queue if it's in upload queue)
   *
   * @param jQuery file_row
   */
  var remove_file = function (file_row) {
    var wrapper = $(this);

    var real_id = file_row.attr('real_id');
    var uploading_id = file_row.attr('uploading_id');

    if (real_id) {
      wrapper.append('<input type="hidden" name="' + this.fu_variables.form_delete_field_name + '" value="' + real_id + '" class="attachment_pending_deletion" />');
    } else {
      this.fu_variables.uploader.removeFile(uploading_id);
    } // if

    file_row.remove();
  }; // remove_file

  /**
   * Get file row
   *
   * @param Number file_id
   * @return Object
   */
  var get_file_row = function (file_id) {
    return this.fu_variables.attachment_list.find('tr[uploading_id="' + file_id + '"]:first');
  }; // get_file_row

  /**
   * Update uploading progress
   *
   * @param Object file
   */
  var update_file_progress = function (file) {
    var file_row = get_file_row.apply(this, [file.id]);
    if (file_row.length) {
      file_row.find('span.progressbar span.progressbar_inner').css('width', file.percent + '%');
    } // if
  };

  /**
   * Finalize uploading file
   *
   * @param Object file
   */
  var finalize_file_upload = function (file, uploaded_file) {
    var file_row = get_file_row.apply(this, [file.id]);
    if (file_row.length) {
      file_row.find('span.progressbar').remove();
      file_row.attr('uploading_id', '').attr('real_id', uploaded_file.id).removeClass('uploading');
      file_row.find('td.filename').append('<input type="hidden" name="' + this.fu_variables.form_field_name + '" value="' + uploaded_file.id + '" />');
    } // if
  }; // finalize_file_progress

  /**
   * on upload error
   *
   * @param Object error
   */
  var upload_error = function (error) {
    var file = error.file;
    var file_row = get_file_row.apply(this, [file.id]);

    if (file_row.length) {
      file_row.remove();
    } // if

    if (error.message) {
      App.Wireframe.Flash.error(error.message);
    } else {
      App.Wireframe.Flash.error('Upload Failed');
    } // if

    this.fu_variables.uploader.refresh();
  }; // upload_error

  /**
   * Reset this uploader
   */
  var reset = function () {
    var wrapper = $(this);
    this.fu_variables.uploader.stop();
    this.fu_variables.uploader.splice(0, this.fu_variables.uploader.files.length);
    this.fu_variables.uploader.refresh();

    this.fu_variables.attachment_list.empty();
    wrapper.find('input.attachment_pending_deletion').remove();
  }; // reset this widget

/**********************************************************************************************************************/

  $.fn.selectAttachmentsAdvanced = function(method) {
    var plugin_arguments = arguments;
    if ( methods[method] ) {
      return methods[method].apply( this, Array.prototype.slice.call( plugin_arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return methods.init.apply( this, plugin_arguments );
    } else {
      $.error( 'Method ' +  method + ' does not exist on jQuery.tooltip' );
    } // if
  };

})(jQuery);