(function ($) {
  $.drappers = {};

  $.fn.drapper = function (options) {
    options = $.extend({}, options);
    options.drapperIdentifier = "data-drapper";
    options.elementSelector = options.elementSelector || '[' + options.drapperIdentifier + ']';

    var drapperMethods = function (decoree) {
      var _hashConfig = (function () {
        var _rawAttributeData = $(decoree).attr(options.drapperIdentifier);
        var result = {};
        var _attributeData;

        try {
          _attributeData = eval("({" + _rawAttributeData + "})"); //TODO: eval is evil
        } catch (e) {
          throw ("Drapper attributes are invalid. It should be as 'drapperName: config'. 'config' is a JSON. \n" +
                 "Original error message: \n" + e.message);
        }

        $.each(_attributeData, function (key, value) {
          result['drapperType'] = key;
          result['drapperConfig'] = value;
        });
        return result;
      }());

      var _type = function () { return _hashConfig.drapperType; };
      var config = function () { return _hashConfig.drapperConfig; };

      function wrapper() {
        var findFakeDiv = function () {
          var a = $(decoree).next('div[data-drapper-wrapper]');
          if (a.length === 0) {
            return false;
          } else {
            return a;
          }
        };
        var createFakeDiv = function () {
          var fakeDiv = $(document.createElement('div'));
          fakeDiv.attr({
            'data-drapper-wrapper': true,
            'class': 'drapperWrapper'
          });
          $(decoree).after(fakeDiv);
          return fakeDiv;
        };
        return findFakeDiv() || createFakeDiv();
      }

      function drapperConverter() {
        var converterPlugin = $.drappers[_type()];

        if (converterPlugin === undefined) {
          throw "Undefined drapper extension '" + _type() + "'. " +
                "Please, provide 'jquery.drapper.'" + _type() + "' plugin. " +
                "See https://github.com/aratak/jquery_drapper/ for details.";
        }

        return converterPlugin.call(decoree);
      }

      function init() {
        $.extend(decoree, {
          'isDecoree': true,
          'drapperType': _type(),
          'config': config(),
          'wrapper': wrapper
        });
        return drapperConverter();
      }

      return init();
    };

    return this.each(function () {
      $(this).find(options.elementSelector).each(function () {
        if (!this.isDecoree) {
          drapperMethods(this);
        }
      });
    });
  };
}(jQuery));