EventEmitter = (require 'events').EventEmitter
ListNode = (require '../list/ListNode.coffee').ListNode
ExodusError = (require '../errors/ExodusError.coffee').ExodusError

class Cursor extends EventEmitter
  # @private
  store: undefined
  # @private
  txn: undefined
  # @private
  node: undefined
  # @private
  begin: undefined
  # @private
  end: undefined
  # @private
  isClosed: undefined

  @create$Store$TransactionDescriptor: (store, txn, o) ->
    if !o? then o = new Cursor()
    o.store = store
    o.txn = txn
    o.node = ListNode.create$Log store.getList().log
    o.node.next = store.getList().rootNode.next
    o.node.nextAddr = store.getList().rootNode.nextAddr
    o.begin = true
    o.end = false
    o.isClosed = false
    return o

  # Move to next record.
  #
  # @return boolean
  getNext$emit: () ->
    oldnode = @node
    @node = @node.next
    if @node?
      @node.once 'getNode', (ok, newnode) =>
        if ok? && !newnode.isEmpty()
          @begin = false
          @node = newnode
          @node.prev = oldnode
          @emit 'getNext', true
        else
          @end = true
          @node = oldnode
          @emit 'getNext', false
      @node.getNode()
    else
      @end = true
      @emit 'getNext', @node?

  # Move to next record.
  #
  # @return boolean
  # TODO getNextDup: () ->

  # TODO getNextNoDup: () ->

  getLast$emit: () ->
    @begin = false
    @end = true
    if @node.next?
      @once 'getNext', (success) =>
        if success
          @getLast$emit()
        else
          @emit 'getLast', true
      @getNext$emit()
    else
      @emit 'getLast', true

  # Move to previous record.
  #
  # @return
  getPrev: () ->
    if @node.prev? && !@node.prev.keyAddr.equals$Addr64 @node.nullAddr
      @node = @node.prev
      @end = false
      return true
    else
      @begin = true
      return false

  # Move to previous record.
  #
  # @return
  # TODO getPrevDup: () ->

  # TODO getPrevNoDup: () ->

  getKey: () ->
    return @node.key

  getValue: () ->
    return @node.value

  # ByteIterable Nullable
  # TODO getSearchKey$ByteIterable: (key) ->

  # ByteIterable Nullable
  # TODO getSearchKeyRange$ByteIterable: (key) ->

  # boolean
  # TODO getSearchBoth$ByteIterable$ByteIterable: (key, value) ->

  # ByteIterable Nullable
  # TODO getSearchBothRange$ByteIterable$ByteIterable: (key, value) ->

  count: () ->
    return @store.count()

  close: () ->
    if @isClosed
      throw new ExodusError "Cursor is already closed"
    @isClosed = true

  deleteCurrent: () ->
    if @begin
      return false
    else
      @node = @store.getList().deleteNode$ListNode @node
      return true

exports.Cursor = Cursor