;(function () { // closure for web browsers

if (module) {
  module.exports = LRUCache
} else {
  // just set the global for non-node platforms.
  ;(function () { return this })().LRUCache = LRUCache
}

function hOP (obj, key) {
  return Object.prototype.hasOwnProperty.call(obj, key)
}

function LRUCache (maxLength) {
  if (!(this instanceof LRUCache)) {
    return new LRUCache(maxLength)
  }
  var cache = {} // hash of items by key
    , lruList = {} // list of items in order of use recency
    , lru = 0 // least recently used
    , mru = 0 // most recently used
    , length = 0 // number of items in the list

  // resize the cache when the maxLength changes.
  Object.defineProperty(this, "maxLength",
    { set : function (mL) {
        if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
        maxLength = mL
        // if it gets above double maxLength, trim right away.
        // otherwise, do it whenever it's convenient.
        if (length > maxLength) trim()
      }
    , get : function () { return maxLength }
    , enumerable : true
    })

  this.maxLength = maxLength

  Object.defineProperty(this, "length",
    { get : function () { return length }
    , enumerable : true
    })

  this.reset = function () {
    cache = {}
    lruList = {}
    lru = 0
    mru = 0
    length = 0
  }

  this.set = function (key, value) {
    if (hOP(cache, key)) {
      this.get(key)
      cache[key].value = value
      return undefined
    }
    var hit = {key:key, value:value, lu:mru++}
    lruList[hit.lu] = cache[key] = hit
    length ++
    if (length > maxLength) trim()
  }

  this.get = function (key) {
    if (!hOP(cache, key)) return undefined
    var hit = cache[key]
    delete lruList[hit.lu]
    if (hit.lu === lru) lruWalk()
    hit.lu = mru ++
    lruList[hit.lu] = hit
    return hit.value
  }

  this.del = function (key) {
    if (!hOP(cache, key)) return undefined
    var hit = cache[key]
    delete cache[key]
    delete lruList[hit.lu]
    if (hit.lu === lru) lruWalk()
    length --
  }

  function lruWalk () {
    // lru has been deleted, hop up to the next hit.
    lru = Object.keys(lruList).shift()
  }

  function trim () {
    if (length <= maxLength) return undefined
    var prune = Object.keys(lruList).slice(0, length - maxLength)
    for (var i = 0, l = (length - maxLength); i < l; i ++) {
      delete cache[ lruList[prune[i]].key ]
      delete lruList[prune[i]]
    }
    length = maxLength
    lruWalk()
  }
}

})()
