luavi = {}

local complete = require('luavi.complete')

function luavi.complete(findstart, base)
    complete.complete(findstart, base)
end

local function fold_iter(buf, fromline, toline)
  assert(fromline == nil or type(fromline) == "number", "fromline must be a number if specified!")
  buf = buf or vimutils.current_buffer()
  toline = toline or #buf
  assert(type(toline) == "number", "toline must be a number if specified!")

  local lineidx = fromline and (fromline - 1) or 0
  -- to remember consecutive folds
  local foldlist = {}
  -- closure blocks opening/closing statements
  local patterns = {{"do", "end"},
                    {"repeat", "until%s+.+"},
                    {"if%s+.+%s+then", "end"},
                    {"for%s+.+%s+do", "end"},
                    {"function.+", "end"},
                    {"return%s+function.+", "end"},
                    {"local%s+function%s+.+", "end"},
                   }

  return function()
    lineidx = lineidx + 1
    if lineidx <= toline then
      -- search for one of blocks statements
      for i, t in ipairs(patterns) do
        -- add whole line anchors
        local tagopen = '^%s*' .. t[1] .. '%s*$'
        local tagclose = '^%s*' .. t[2] .. '%s*$'
        -- try to find opening statement
        if string.find(buf[lineidx], tagopen) then
          -- just remember it
          table.insert(foldlist, t)
        elseif string.find(buf[lineidx], tagclose) then     -- check for closing statement
          -- Proceed only if there is unclosed block in foldlist and its
          -- closing statement matches.
          if #foldlist > 0 and string.find(buf[lineidx], foldlist[#foldlist][2]) then
            table.remove(foldlist)
            -- Add 1 to foldlist length (synonymous to fold level) to include
            -- closing statement in the fold too.
            return #foldlist + 1, lineidx, buf[lineidx]
          else
            -- An incorrect situation where opening/closing statements didn't
            -- match (probably due to malformed formating or erroneous code).
            -- Just "reset" foldlist.
            foldlist = {}
          end
        end
      end
      -- #foldlist is fold level
      return #foldlist, lineidx, buf[lineidx]
    end
  end
end

function luavi.fold(linenum)
  assert(type(linenum) == "number", "linenum must be a number!")

  -- by default don't make nested folds
  local innerfolds = false
  -- though a configuration variable can enable it
  if vimutils.eval('exists("b:lua_inner_folds")') == 1 then
    innerfolds = vimutils.eval('b:lua_inner_folds') == 1
  elseif vimutils.eval('exists("g:lua_inner_folds")') == 1 then
    innerfolds = vimutils.eval('g:lua_inner_folds') == 1
  end

  for lvl, lineidx in fold_iter() do
    if lineidx == linenum then
      vim.command("return " .. (innerfolds and lvl or (lvl > 1 and 1 or lvl)))
      break
    end
  end
end

return luavi