# requires mongo > 1.3: see # http://www.mongodb.org/display/DOCS/findandmodify+Command
#
# usage:
#
#   Sequence.next('foobar') #=> 1
#   Sequence.next('foobar') #=> 2
#

class Sequence < App::Model
  class Error < StandardError; end

  ensure_index :name, :unique => true

  def Sequence.cache
    load_cache! unless defined?(@cache)
    @cache
  end

  def Sequence.load_cache!
    @cache = (
      all.inject(Hash.new){|hash, sequence| hash.update(sequence.name => sequence)}
    )
  end

  def Sequence.for(name)
    name = name.to_s
    record = cache[name] || first(:name => name)
    return record if record

    begin
      record = new(:name => name, :value => 0)
      record.save(:safe => true)
    rescue
      record = first(:name => name) or raise(Error, "no sequence for #{ name }!")
    end

    cache['name'] = record
  end

  def Sequence.[](name)
    Sequence.for(name)
  end

  def Sequence.next(name)
    Sequence.for(name).next
  end

  def next
    result =
      Sequence.find_and_modify(
        'query' => { 'name' => name },
        'update' => { '$inc' => {'value' => 1} },
        'new' => true
      )
    self.value = result['value']
  end

  def Sequence.find_and_modify(options = {})
    oh = OrderedHash.new
    oh['findandmodify'] = collection.name
    oh.merge!(options)
    db = collection.db
    result = db.command(oh)
    unless result["ok"] == 1
      raise(Error, "find_and_modify failed with: #{ result['errmsg'] }")
    end
    result['value']
  end
end