CompressedUnsignedLongByteIterable =
  (require '../log/iterate/CompressedUnsignedLongByteIterable.coffee').
  CompressedUnsignedLongByteIterable
DataIterator = (require '../log/DataIterator.coffee').DataIterator
LightReadIterator = (require '../log/DataIterator.coffee').LightReadIterator
LoggableToWrite = (require '../log/LoggableToWrite.coffee').LoggableToWrite
RandomAccessLoggable =
  (require '../log/RandomAccessLoggable.coffee').RandomAccessLoggable
LightOutputStream =
  (require '../database/impl/iterate/LightOutputStream.coffee').
  LightOutputStream
Addr64 = (require '../log/Addr64.coffee').Addr64

# This class is necessary for commmiting log. It is written in log before each
# commit and contains kind of check sum. If it is valid, everything was written
# correctly.
class DatabaseRoot extends RandomAccessLoggable
  @DATABASE_ROOT_TYPE: 1
  @MAGIC_DIFF: 199
  # @private
  rootAddress: undefined
  # @private
  lastStructureId: undefined
  # @private
  isValid: undefined

  # Constructor takes rootAddress and lastStructureId.
  @create$Addr64$int: (rootAddress, lastStructureId, o) ->
    if !o? then o = new DatabaseRoot()
    o.rootAddress = rootAddress
    o.lastStructureId = lastStructureId
    o.isValid = undefined
    return o

  getRootAddress: () ->
    return @rootAddress

  getLastStructureId: () ->
    return @lastStructureId

  isValid: () ->
    return @isValid

  # Get WriteLogable that can be written to log.
  toLoggable: () ->
    dataLength =
      CompressedUnsignedLongByteIterable.
      getCompressedSize$int(@rootAddress.toFloat()) +
      CompressedUnsignedLongByteIterable.
      getCompressedSize$int(@lastStructureId) +
      CompressedUnsignedLongByteIterable.
      getCompressedSize$int(@rootAddress.toFloat() + @lastStructureId +
      DatabaseRoot.MAGIC_DIFF)
    los = LightOutputStream.create$int dataLength
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @rootAddress.toFloat(), los
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @lastStructureId, los
    CompressedUnsignedLongByteIterable.
    fillBytes$int$LightOutputStream @rootAddress.toFloat() +
    @lastStructureId + DatabaseRoot.MAGIC_DIFF, los
    return LoggableToWrite.
    create$int$ByteIterable$int DatabaseRoot.DATABASE_ROOT_TYPE,
            los.asArrayByteIterable()

  # Temporary function, until I undestand Loggable factories and usage of
  # static temp emitters.
  # Very DANGEROUS and should be rewritten anyway!!!
  readFromLoggable: (loggable) ->
    it = loggable.getData().iterator()
    nums = new Object()
    emit = false
    onGetAllNumbers = () =>
      @rootAddressInt = nums[1]
      @rootAddress = Addr64.create$int$int(0, 0).plus$int(@rootAddressInt)
      @lastStructureId = nums[2]
      @isValid = @rootAddressInt == nums[3] -
      @lastStructureId - DatabaseRoot.MAGIC_DIFF
      if emit
        @emit 'readFromLoggable', @isValid
    k = 0
    CompressedUnsignedLongByteIterable.EMITTER.once 'getInt', (value) =>
      nums[k] = value
      if k != 3
        for i in [k + 1..3]
          nums[i] = CompressedUnsignedLongByteIterable.
          getInt$ByteIterator$emit it
      onGetAllNumbers()
    while k != 3
      k += 1
      nums[k] = CompressedUnsignedLongByteIterable.
      getInt$ByteIterator$emit it
      if !nums[k]?
        emit = true
        return undefined
    CompressedUnsignedLongByteIterable.EMITTER.removeAllListeners 'getInt'
    onGetAllNumbers()
    return @isValid

exports.DatabaseRoot = DatabaseRoot