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

utils = require '../TestUtils.coffee'
Addr64 = (require SRC_DIR + 'Addr64.coffee').Addr64
Cache = (require SRC_DIR + 'LogCache.coffee').LogCache
LoggableToWrite = (require SRC_DIR + 'LoggableToWrite').LoggableToWrite
NullLoggable = (require SRC_DIR + 'NullLoggable').NullLoggable
RandomAccessLoggable =
  (require SRC_DIR + 'RandomAccessLoggable').RandomAccessLoggable
ArrayByteIterable =
  (require SRC_DIR + '../database/impl/iterate/ArrayByteIterable.coffee').
  ArrayByteIterable
LogConfig = (require SRC_DIR + 'LogConfig.coffee').LogConfig
Log = (require SRC_DIR + 'Log').Log
LogUtil = (require SRC_DIR + 'LogUtil').LogUtil

nameSize = 13
N = 1000
Nstep = 100
Nread = 50
NPages = 10
path = new Object()
log = new Object()
readFirst = false
readSecond = false
map = new Object()
addrs = new Object()
commitAddrs = new Object()
config = new Object()

writeToLogWithCommits = () =>
  map = new Object()
  addrs = new Object()
  commitAddrs = new Object()
  writeFromJtoK = (j, k) =>
    log.once 'flush', () =>
      writeFromJtoK j + Nstep, k + Nstep
    if k <= N
      for i in [j..k]
        b = ArrayByteIterable.create$Buffer new Buffer("Hello " + i.toString())
        d = LoggableToWrite.create$int$ByteIterable$int i, b, i
        addrs[i] = log.write$Loggable d
        map[addrs[i].toFloat().toString()] = d.data.toString()
      commitAddrs[k / Nstep] = log.flush$emit()
    else
      log.close$emit()
  writeFromJtoK 1, Nstep

exports.setUp = (callback) ->
  path = utils.getNewDirName()
  config = LogConfig.create()
  config.setDir path
  config.setFileSize 1
  config.setCachePageSize 256
  config.setCacheOpenFilesCount NPages
  log = Log.create$LogConfig config
  callback()
  
exports.testCreation = (test) ->
  test.expect 2
  test.equal log.directory, path
  test.equal log.fileSize, 1
  test.done()
  
exports.testClosingAndReadingFromTheSameLog = (test) ->
  test.expect Nread
  nForRead = 0
  nCommitsInitialized = 0

  tryToFinish = () =>
    if readFirst
      test.done()

  log.on 'close', (fn) =>
    for i in [1..Nread]
      r = i * (N / Nread) - Math.floor Math.random() * (N / Nread)
      addrString = Addr64.addr64ToString$Addr64$int(addrs[r],
              LogUtil.LOG_NAME_BASE)
      log.once 'read' + addrString, (loggable) =>
        onRead(loggable)
      log.read$Addr64$emit addrs[r]

  onRead = (loggable) ->
    if loggable.type != 0
      test.equal loggable.structureId, loggable.type
    nForRead += 1
    if (nForRead == Nread)
      readFirst = true
    tryToFinish()

  writeToLogWithCommits()

exports.testClosingAndReadingFromNewLog = (test) =>
  test.expect 2 * Nread
  log.once 'close', (fn) =>
    logTmp = Log.create$LogConfig config
    nForLogTmp = 0
    onRead = (loggable) =>
      test.ok loggable.type >= 1 && loggable.type <= N
      test.equal loggable.structureId, loggable.type
      nForLogTmp += 1
      if nForLogTmp == Nread
        test.done()
    for i in [1..Nread]
      r = i * (N / Nread) - Math.floor Math.random() * (N / Nread)
      addrString = Addr64.addr64ToString$Addr64$int(addrs[r],
              LogUtil.LOG_NAME_BASE)
      logTmp.once 'read' + addrString, (loggable) =>
        onRead loggable
      logTmp.read$Addr64$emit addrs[r]
  writeToLogWithCommits()

exports.testGetLastLoggableOfType = (test) =>
  test.expect 2
  k = N / 10
  log.once 'close', (fn) =>
    log.once 'getLastLoggableOfType', (loggable) ->
      test.equal loggable.getType(), k
      test.equal loggable.getStructureId(), k
      test.done()
    log.getLastLoggableOfType$int$emit(k)
  writeToLogWithCommits()

