var _ = require('underscore'),
    utils = require('../utils'),
    isTag = utils.isTag,
    decode = utils.decode,
    encode = utils.encode,
    rspace = /\s+/,

    // Attributes that are booleans
    rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i;


var setAttr = function(el, name, value) {
  if (typeof name === 'object') return _.extend(el.attribs, name);

  if (value === null) {
    removeAttribute(el, name);
  } else {
    el.attribs[name] = encode(value);
  }

  return el.attribs;
};

var attr = exports.attr = function(name, value) {
  var elem = this[0];

  if (!elem || !isTag(elem))
    return undefined;

  if (!elem.attribs) {
    elem.attribs = {};
  }

  // Return the entire attribs object if no attribute specified
  if (!name) {
    for (var a in elem.attribs) {
      elem.attribs[a] = decode(elem.attribs[a]);
    }
    return elem.attribs;
  }

  // Set the value (with attr map support)
  if (typeof name === 'object' || value !== undefined) {
    this.each(function(i, el) {
      el.attribs = setAttr(el, name, value);
    });
    return this;
  } else if (Object.hasOwnProperty.call(elem.attribs, name)) {
    // Get the (decoded) attribute
    return decode(elem.attribs[name]);
  }
};

/**
 * Remove an attribute
 */

var removeAttribute = function(elem, name) {
 if (!isTag(elem.type) || !elem.attribs || !Object.hasOwnProperty.call(elem.attribs, name))
   return;

 if (rboolean.test(elem.attribs[name]))
   elem.attribs[name] = false;
 else
   delete elem.attribs[name];
};


var removeAttr = exports.removeAttr = function(name) {
  this.each(function(i, elem) {
    removeAttribute(elem, name);
  });

  return this;
};

var hasClass = exports.hasClass = function(className) {
  return _.any(this, function(elem) {
    var attrs = elem.attribs;
    return attrs && _.contains((attrs['class'] || '').split(rspace), className);
  });
};

var addClass = exports.addClass = function(value) {
  // Support functions
  if (_.isFunction(value)) {
    this.each(function(i) {
      var className = this.attr('class') || '';
      this.addClass(value.call(this, i, className));
    });
  }

  // Return if no value or not a string or function
  if (!value || !_.isString(value)) return this;

  var classNames = value.split(rspace),
      numElements = this.length,
      numClasses,
      setClass,
      $elem;


  for (var i = 0; i < numElements; i++) {
    $elem = this.make(this[i]);
    // If selected element isnt a tag, move on
    if (!isTag(this[i])) continue;

    // If we don't already have classes
    if (!$elem.attr('class')) {
      $elem.attr('class', classNames.join(' ').trim());
    } else {
      setClass = ' ' + $elem.attr('class') + ' ';
      numClasses = classNames.length;

      // Check if class already exists
      for (var j = 0; j < numClasses; j++) {
        if (!~setClass.indexOf(' ' + classNames[j] + ' '))
          setClass += classNames[j] + ' ';
      }

      $elem.attr('class', setClass.trim());
    }
  }

  return this;
};

var removeClass = exports.removeClass = function(value) {
  var split = function(className) {
    return className ? className.trim().split(rspace) : [];
  };

  var classes = split(value);

  // Handle if value is a function
  if (_.isFunction(value)) {
    return this.each(function(i, el) {
      this.removeClass(value.call(this, i, el.attribs['class'] || ''));
    });
  }

  return this.each(function(i, el) {
    if (!isTag(el)) return;
    el.attribs['class'] = (!value) ? '' : _.reject(
      split(el.attribs['class']),
      function(name) { return _.contains(classes, name); }
    ).join(' ');
  });
};
