SRC_DIR = '../../src/'

utils = require '../TestUtils.coffee'
Log = (require SRC_DIR + 'log/Log.coffee').Log
LogConfig = (require SRC_DIR + 'log/LogConfig.coffee').LogConfig
List = (require SRC_DIR + 'list/List.coffee').List
ArrayByteIterable =
  (require SRC_DIR + 'database/impl/iterate/ArrayByteIterable.coffee').
  ArrayByteIterable

# Constants
N = 20
N2 = 10
address = 560

log = new Object()

pushPartOne$List = (list) =>
  for i in [1..N]
    list.push$ByteIterable$ByteIterable$emit ArrayByteIterable.create$Buffer(new Buffer(i.toString())),
            ArrayByteIterable.create$Buffer(new Buffer("Value " + i.toString()))

pushPartTwo$List = (list) =>
  for i in [N - N2 + 1..N]
    list.push$ByteIterable$ByteIterable$emit ArrayByteIterable.create$Buffer(new Buffer(i.toString())),
            ArrayByteIterable.create$Buffer(new Buffer("Value " +
            (i + 1).toString()))

writeToLog$List = (list) =>
  list.flush$emit()

getAllRecords$List = (list) =>
  for i in [1..N]
    list.get$ByteIterable$emit ArrayByteIterable.create$Buffer(new Buffer(i.toString()))

exports.setUp = (callback) ->
  path = utils.getNewDirName()
  config = LogConfig.create()
  config.setDir path
  config.setFileSize 1
  config.setCachePageSize 256
  config.setCacheOpenFilesCount 100
  log = Log.create$LogConfig config
  second = false
  callback()

exports.testAddingInMemory = (test) ->
  test.expect N * 3 + 3 + 1
  List.EMITTER.once 'create', (list) =>
    pushPartOne$List list
    test.equal log.getHighAddress().toFloat(), 0
    test.equal log.fileSize, 1
    test.equal list.length, N
    node = list.rootNode.next
    for i in [N..1]
      test.equal node.key.getBytesUnsafe().toString(), i.toString()
      test.equal node.value.getBytesUnsafe().toString(), "Value " + i.toString()
      test.ok node.ownAddr.equals$Addr64 node.nullAddr
      node = node.next
    test.equal node, undefined
    test.done()
  List.create$Log$Boolean$int log, false, 5

exports.testWritingAndAdding = (test) ->
  test.expect N + 2 + 1
  List.EMITTER.once 'create', (list) =>
    pushPartOne$List list
    num = N
    onPush = () =>
      test.equal list.length, N
      node = list.rootNode.next
      for i in [N..N - N2 + 1]
        test.ok node.ownAddr.equals$Addr64 node.nullAddr
        node = node.next
      for i in [N - N2..1]
        test.ok !(node.ownAddr.equals$Addr64 node.nullAddr)
        node = node.next
      test.equal node, undefined
      test.done()
    list.on 'flush', () =>
      test.equal log.getHighAddress().toFloat(), address
      pushPartTwo$List list
    list.on 'push', (node) =>
      num += 1
      if num == N + N2
        onPush()
    writeToLog$List list
  List.create$Log$Boolean$int log, false, 5

exports.testGetting = (test) ->
  test.expect 2 * N2 + 1
  List.EMITTER.once 'create', (list) =>
    pushPartOne$List list
    num = N
    list.once 'flush', () =>
      test.equal log.getHighAddress().toFloat(), address
      pushPartTwo$List list
    list.on 'push', (node) =>
      num += 1
      if num == N + N2
        getAllRecords$List list
    list.on 'get', (node) =>
      key = parseInt node.key.getBytesUnsafe().toString()
      if key > N - N2
        test.equal node.value.getBytesUnsafe().toString(),
                "Value " + (key + 1).toString()
      else
        test.equal node.value.getBytesUnsafe().toString(),
                "Value " + key.toString()
      num -= 1
      if num == N - N2
        test.done()
    writeToLog$List list
  List.create$Log$Boolean$int log, false, 5

exports.testDelete = (test) ->
  test.expect 2
  List.EMITTER.once 'create', (list) =>
    num = 0
    list.on 'push', (node) =>
      num += 1
      if num == N
        writeToLog$List list
    list.once 'get', (node) =>
      test.ok !node?
      test.done()
    list.once 'delete', (key) =>
      test.equal list.length, N - 1
      list.get$ByteIterable$emit ArrayByteIterable.
      create$Buffer(new Buffer((10).toString()))
    list.once 'add', () =>
      list.delete$ByteIterable$emit ArrayByteIterable.
      create$Buffer(new Buffer((10).toString()))
    list.once 'flush', () =>
      list.add$ByteIterable$ByteIterable$emit ArrayByteIterable.
      create$Buffer(new Buffer((10).toString())), ArrayByteIterable.
      create$Buffer(new Buffer((10).toString()))
    pushPartOne$List list

  List.create$Log$Boolean$int log, false, 5
