ByteIterableBase =
  (require '../../database/impl/iterate/ByteIterableBase.coffee').
  ByteIterableBase
RandomAccessByteIterable =
  (require '../RandomAccessByteIterable.coffee').RandomAccessByteIterable
ByteIterator =
  (require '../../database/ByteIterator.coffee').ByteIterator
ExodusError = (require '../../errors/ExodusError.coffee').ExodusError

# This class allows working with fixed parts of other iterables.
class FixedLengthByteIterable extends ByteIterableBase
  # @private
  source: undefined
  # @private
  offset: undefined

  # Constructor.
  #
  # @param source the base ByteIterable.
  # @param offset the offset inside iterable.
  # @param length the length of data of the source to be used.
  @create$ByteIterable$int$int: (source, offset, length, o) ->
    if length < 0
      throw new ExodusError("ByteIterable length can't be less than zero")
    if !o? then o = new FixedLengthByteIterable
    o.source = source
    o.offset = offset
    o.length = length
    return o

  # Constructor.
  #
  # @param source the base ByteIterable.
  # @param length the length of data of the source to be used.
  @create$ByteIterable$int: (source, length, o) ->
    return FixedLengthByteIterable.create$ByteIterable$int$int source, 0,
            length, o

  # Compare this iterable to another.
  #
  # @param right another iterable.
  # @return always emits 'compare' event with number (see
  # ByteIterableUtil.compare$ByteIterable$ByteIterable).
  compareTo$ByteIterable$emit: (right) ->
    onCompare = (res) =>
      @emit 'compare', res
    if @source instanceof RandomAccessByteIterable
      @source.once 'compare', (res) =>
        onCompare res
      res = @source.compare$int$int$ByteIterable$emit 0, @length, right
      if res?
        @source.removeAllListeners 'compare'
        onCompare res
    else
      res = @source.compareTo$ByteIterable$emit right
      onCompare res

  getLength: () ->
    return @length

  # @protected
  getIterator: () ->
    if @length == 0
      return ByteIterableBase.EMPTY_ITERATOR
    bi = @source.iterator()
    bi.skip$int @offset
    res = new ByteIterator()
    res.i = @length
    res.hasNext$emit = () ->
      bi.once 'hasNext', (hasNext) =>
        @emit 'hasNext',  @i > 0 && hasNext
      hasNext = bi.hasNext$emit()
      if hasNext?
        bi.removeAllListeners 'hasNext'
        return @i > 0 && hasNext
      else
        return undefined
    res.next$emit = () ->
      @i -= 1
      bi.once 'next', (next) =>
        @emit 'next', next
      next = bi.next$emit()
      if next?
        bi.removeAllListeners 'next'
        return next
      else
        return undefined
    res.skip$int = (length) ->
      result = bi.skip Math.min @length, @i
      @i -= result
      return result
    return res

exports.FixedLengthByteIterable = FixedLengthByteIterable