## Copyright (c) 2011, Chris Umbel
## 
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
## 
## The above copyright notice and this permission notice shall be included in
## all copies or substantial portions of the Software.
## 
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
## THE SOFTWARE.

WordNetFile = require('./wordnet_file')
fs = require('fs')
util = require('util')


getFileSize = (path) ->
  stat = fs.statSync(path)
  stat.size


findPrevEOL = (fd, pos, callback) ->
  buff = new Buffer(1024);
  if pos == 0
    callback(0)
  else
    fs.read fd, buff, 0, 1, pos, (err, count) ->
      if buff[0] == 10
        callback(pos + 1)
      else
        findPrevEOL(fd, pos - 1, callback)


readLine = (fd, pos, callback) ->
  buff = new Buffer(1024)
  findPrevEOL fd, pos, (pos) ->
    WordNetFile.appendLineChar fd, pos, 0, buff, callback


miss = (callback) -> 
  callback({status: 'miss'})


findAt = (fd, size, pos, lastPos, adjustment, searchKey, callback, lastKey) ->
  if lastPos == pos || pos >= size
    miss callback
  else
    readLine fd, pos, (line) ->
      tokens = line.split(/\s+/)
      key = tokens[0]

      if key == searchKey
        callback {status: 'hit', key: key, 'line': line, tokens: tokens}
      else if adjustment == 1 || key == lastKey
        miss callback
      else
        adjustment = Math.ceil(adjustment * 0.5)

        if key < searchKey
          findAt fd, size, pos + adjustment, pos, adjustment, searchKey, callback, key
        else
          findAt fd, size, pos - adjustment, pos, adjustment, searchKey, callback, key


find = (searchKey, callback) ->
  indexFile = this

  indexFile.open (err, fd) ->
    if err
      console.log(err);
    else
      size = getFileSize(indexFile.filePath) - 1
      pos = Math.ceil(size / 2)
      findAt fd, size, pos, null, pos, searchKey, (result)->
        callback(result)


lookupFromFile = (word, callback) ->
  @find word, (record) ->
    indexRecord = null

    if record.status == 'hit'
      ptrs = []
      offsets = []

      for i in [0..parseInt(record.tokens[3]) - 1] by 1
        ptrs.push(record.tokens[i])

      for i in [0..parseInt(record.tokens[2]) - 1] by 1
        offsets.push(parseInt(record.tokens[ptrs.length + 6 + i], 10))

      indexRecord = {
        lemma: record.tokens[0]
        pos: record.tokens[1]
        ptrSymbol: ptrs
        senseCnt:  parseInt(record.tokens[ptrs.length + 4], 10)
        tagsenseCnt:  parseInt(record.tokens[ptrs.length + 5], 10)
        synsetOffset:  offsets
      }

    callback(indexRecord)


lookup = (word, callback) ->
  @lookupFromFile(word, callback)


IndexFile = (dataDir, name) ->
  WordNetFile.call(this, dataDir, 'index.' + name)


util.inherits(IndexFile, WordNetFile)

IndexFile.prototype.lookupFromFile = lookupFromFile
IndexFile.prototype.lookup = lookup
IndexFile.prototype.find = find

IndexFile.prototype._findAt = findAt

module.exports = IndexFile
