'use strict';

var STRING_DASHERIZE_REGEXP = (/[ _]/g);
var STRING_DASHERIZE_CACHE = {};
var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g);
var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g);
var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g);
var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g);

/**
  Converts a camelized string into all lower case separated by underscores.

  ```javascript
  decamelize('innerHTML');         // 'inner_html'
  decamelize('action_name');       // 'action_name'
  decamelize('css-class-name');    // 'css-class-name'
  decamelize('my favorite items'); // 'my favorite items'
  ```

  @method decamelize
  @param {String} str The string to decamelize.
  @return {String} the decamelized string.
*/
function decamelize(str) {
  return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase();
}

/**
  Replaces underscores, spaces, or camelCase with dashes.

  ```javascript
  dasherize('innerHTML');         // 'inner-html'
  dasherize('action_name');       // 'action-name'
  dasherize('css-class-name');    // 'css-class-name'
  dasherize('my favorite items'); // 'my-favorite-items'
  ```

  @method dasherize
  @param {String} str The string to dasherize.
  @return {String} the dasherized string.
*/
function dasherize(str) {
  var cache = STRING_DASHERIZE_CACHE,
      hit   = cache.hasOwnProperty(str),
      ret;

  if (hit) {
    return cache[str];
  } else {
    ret = decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-');
    cache[str] = ret;
  }

  return ret;
}

/**
  Returns the lowerCamelCase form of a string.

  ```javascript
  camelize('innerHTML');          // 'innerHTML'
  camelize('action_name');        // 'actionName'
  camelize('css-class-name');     // 'cssClassName'
  camelize('my favorite items');  // 'myFavoriteItems'
  camelize('My Favorite Items');  // 'myFavoriteItems'
  ```

  @method camelize
  @param {String} str The string to camelize.
  @return {String} the camelized string.
*/
function camelize(str) {
  return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) {
    return chr ? chr.toUpperCase() : '';
  }).replace(/^([A-Z])/, function(match) {
    return match.toLowerCase();
  });
}

/**
  Returns the UpperCamelCase form of a string.

  ```javascript
  'innerHTML'.classify();          // 'InnerHTML'
  'action_name'.classify();        // 'ActionName'
  'css-class-name'.classify();     // 'CssClassName'
  'my favorite items'.classify();  // 'MyFavoriteItems'
  ```

  @method classify
  @param {String} str the string to classify
  @return {String} the classified string
*/
function classify(str) {
  var parts = str.split('.'),
      out = [];

  for (var i=0, l=parts.length; i<l; i++) {
    var camelized = camelize(parts[i]);
    out.push(camelized.charAt(0).toUpperCase() + camelized.substr(1));
  }

  return out.join('.');
}

/**
  More general than decamelize. Returns the lower\_case\_and\_underscored
  form of a string.

  ```javascript
  'innerHTML'.underscore();          // 'inner_html'
  'action_name'.underscore();        // 'action_name'
  'css-class-name'.underscore();     // 'css_class_name'
  'my favorite items'.underscore();  // 'my_favorite_items'
  ```

  @method underscore
  @param {String} str The string to underscore.
  @return {String} the underscored string.
*/
function underscore(str) {
  return str.replace(STRING_UNDERSCORE_REGEXP_1, '$1_$2').
    replace(STRING_UNDERSCORE_REGEXP_2, '_').toLowerCase();
}

/**
  Returns the Capitalized form of a string

  ```javascript
  'innerHTML'.capitalize()         // 'InnerHTML'
  'action_name'.capitalize()       // 'Action_name'
  'css-class-name'.capitalize()    // 'Css-class-name'
  'my favorite items'.capitalize() // 'My favorite items'
  ```

  @method capitalize
  @param {String} str The string to capitalize.
  @return {String} The capitalized string.
*/
function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.substr(1);
}

module.exports = {
  decamelize: decamelize,
  dasherize: dasherize,
  camelize: camelize,
  classify: classify,
  underscore: underscore,
  capitalize: capitalize
};