fs = require 'fs'
ByteIterableBase = (require './ByteIterableBase.coffee').ByteIterableBase
ByteIterator = (require '../../ByteIterator.coffee').ByteIterator
UnsupportedOperationError =
  (require '../../../errors/UnsupportedOperationError.coffee').
  UnsupportedOperationError

# This class allows working with Buffer as iterable.
class ArrayByteIterable extends ByteIterableBase
  # Constructor.
  #
  # @param bytes the Buffer with data.
  # @param length the length of Buffer or it's part.
  @create$Buffer$int: (bytes, length, o) ->
    if !o? then o = new ArrayByteIterable()
    if length == 0
      o.fillBytes$ByteIterator ByteIterableBase.EMPTY_ITERATOR
    else
      if length == 1
        o.fillBytes$int$ByteIterator bytes[0], ByteIterableBase.EMPTY_ITERATOR
      else
        o.bytes = bytes
        o.length = length
    return o

  # Constructor.
  #
  # @param bytes the Buffer with data.
  @create$Buffer: (bytes, o) ->
    return ArrayByteIterable.create$Buffer$int bytes, bytes.length, o

  # This class implements iterator for this iterable.
  class Iterator extends ByteIterator
    # @private
    offs: undefined
    # @private
    obj: undefined

    # Constructor.
    #
    # @param offset.
    # @param obj the object which this iterator is created inside (to get
    # access to it's properties).
    @create$int: (offset, obj, o) =>
      if !o? then o = new Iterator()
      o.offs = offset
      o.obj = obj
      return o

    hasNext$emit: () =>
      return @offs < @obj.length

    next$emit: () =>
      offset = @offs
      result = @obj.bytes[offset]
      @offs = offset + 1
      return result

    skip$int: (length) =>
      result = Math.min length, @obj.length - @offs
      @offs += result
      return result

    getOffset: () ->
      return @offs

  # @private
  # This class implements empty array iterable (analogue of zero).
  class EmptyIterable extends ArrayByteIterable
    # @private
    ITERATOR: undefined
    # @private
    obj: undefined

    # Constructor.
    #
    # @param obj the object which this iterator is created inside (to get
    # access to it's properties).
    @create: (obj, o) ->
      if !o? then o = new EmptyIterable()
      o = EmptyIterable.create$Buffer$int new Buffer(0), 0, o
      o.obj = obj
      o.ITERATOR = Iterator.create$int 0, o
      return o

    getIterator: () ->
      return @ITERATOR

    setBytes$Buffer: (bytes) ->
      throw new UnsupportedOperationError()

  @EMPTY: EmptyIterable.create()

  # Constructor.
  #
  # @param bi another ByteIterable that will be used as a prototype for new one.
  @create$ByteIterable: (bi, o) ->
    if !o? then o = new ArrayByteIterable()
    length = bi.getLength()
    if length == 0
      o.fillBytes$ByteIterator ByteIterableBase.EMPTY_ITERATOR
    else
      bytes = bi.getBytesUnsafe()
      if length == 1
        o.fillBytes$int$ByteIterator bytes[0], ByteIterableBase.EMPTY_ITERATOR
      else
        o.length = length
        o.bytes = bytes
    return o

  # Constructor.
  #
  # @param it ByteIterator of data.
  @create$ByteIterator: (it, o) ->
    if !o? then o = new ArrayByteIterable()
    o.fillBytes$ByteIterator it
    return o

  # Constructor.
  #
  # @param firstByte.
  # @param bi another ByteIterable that will be used as a prototype for new one.
  @create$int$ByteIterable: (firstByte, bi, o) ->
    if !o? then o = new ArrayByteIterable()
    o.fillBytes$int$ByteIterator firstByte, bi.iterator()
    return o

  # Constructor.
  #
  # @param firstByte.
  # @param it ByteIterator of data.
  @create$int$ByteIterator: (firstByte, it, o) ->
    if !o? then o = new ArrayByteIterable()
    o.fillBytes$int$ByteIterator firstByte, it
    return o

  iterator: () ->
    return @getIterator()

  iterator$int: (offset) ->
    return Iterator.create$int offset, this

  setBytes$Buffer: (bytes) ->
    @bytes = bytes
    @length = bytes.length

  getBytesUnsafe: () ->
    return @bytes

  # TODO this method is never used in exodus. What can I do with it?
  writeTo$ByteArrayOutputStream: (output) ->
    output.write @bytes, 0, @length

  @getEmptyIterator: () ->
    return @EMPTY.ITERATOR

  # @protected
  getIterator: () ->
    return Iterator.create$int 0, this

  # @protected
  fillBytes: () ->
    # do nothing

exports.ArrayByteIterable = ArrayByteIterable