Token = require './token'
CommentScopeRegex = /(\b|\.)comment/

idCounter = 1

module.exports =
class TokenizedLine
  constructor: (properties) ->
    @id = idCounter++

    return unless properties?

    {@openScopes, @text, @tags, @ruleStack, @tokenIterator, @grammar} = properties

  getTokenIterator: -> @tokenIterator.reset(this)

  Object.defineProperty @prototype, 'tokens', get: ->
    iterator = @getTokenIterator()
    tokens = []

    while iterator.next()
      tokens.push(new Token({
        value: iterator.getText()
        scopes: iterator.getScopes().slice()
      }))

    tokens

  tokenAtBufferColumn: (bufferColumn) ->
    @tokens[@tokenIndexAtBufferColumn(bufferColumn)]

  tokenIndexAtBufferColumn: (bufferColumn) ->
    column = 0
    for token, index in @tokens
      column += token.value.length
      return index if column > bufferColumn
    index - 1

  tokenStartColumnForBufferColumn: (bufferColumn) ->
    delta = 0
    for token in @tokens
      nextDelta = delta + token.bufferDelta
      break if nextDelta > bufferColumn
      delta = nextDelta
    delta

  isComment: ->
    return @isCommentLine if @isCommentLine?

    @isCommentLine = false

    for tag in @openScopes
      if @isCommentOpenTag(tag)
        @isCommentLine = true
        return @isCommentLine

    startIndex = 0
    for tag in @tags
      # If we haven't encountered any comment scope when reading the first
      # non-whitespace chunk of text, then we consider this as not being a
      # comment line.
      if tag > 0
        break unless isWhitespaceOnly(@text.substr(startIndex, tag))
        startIndex += tag

      if @isCommentOpenTag(tag)
        @isCommentLine = true
        return @isCommentLine

    @isCommentLine

  isCommentOpenTag: (tag) ->
    if tag < 0 and (tag & 1) is 1
      scope = @grammar.scopeForId(tag)
      if CommentScopeRegex.test(scope)
        return true
    false

  tokenAtIndex: (index) ->
    @tokens[index]

  getTokenCount: ->
    count = 0
    count++ for tag in @tags when tag >= 0
    count

isWhitespaceOnly = (text) ->
  for char in text
    if char isnt '\t' and char isnt ' '
      return false
  return true
