local Range = require 'u.range'
local Renderer = require 'u.renderer'.Renderer

--- @class u.Buffer
--- @field bufnr number
--- @field private renderer u.Renderer
local Buffer = {}

--- @param bufnr? number
--- @return u.Buffer
function Buffer.from_nr(bufnr)
  if bufnr == nil or bufnr == 0 then bufnr = vim.api.nvim_get_current_buf() end

  local renderer = Renderer.new(bufnr)
  return setmetatable({
    bufnr = bufnr,
    renderer = renderer,
  }, { __index = Buffer })
end

--- @return u.Buffer
function Buffer.current() return Buffer.from_nr(0) end

--- @param listed boolean
--- @param scratch boolean
--- @return u.Buffer
function Buffer.create(listed, scratch) return Buffer.from_nr(vim.api.nvim_create_buf(listed, scratch)) end

function Buffer:set_tmp_options()
  self:set_option('bufhidden', 'delete')
  self:set_option('buflisted', false)
  self:set_option('buftype', 'nowrite')
end

--- @param nm string
function Buffer:get_option(nm) return vim.api.nvim_get_option_value(nm, { buf = self.bufnr }) end

--- @param nm string
function Buffer:set_option(nm, val) return vim.api.nvim_set_option_value(nm, val, { buf = self.bufnr }) end

--- @param nm string
function Buffer:get_var(nm) return vim.api.nvim_buf_get_var(self.bufnr, nm) end

--- @param nm string
function Buffer:set_var(nm, val) return vim.api.nvim_buf_set_var(self.bufnr, nm, val) end

function Buffer:line_count() return vim.api.nvim_buf_line_count(self.bufnr) end

function Buffer:all() return Range.from_buf_text(self.bufnr) end

function Buffer:is_empty() return self:line_count() == 1 and self:line(1):text() == '' end

--- @param line string
function Buffer:append_line(line)
  local start = -1
  if self:is_empty() then start = -2 end
  vim.api.nvim_buf_set_lines(self.bufnr, start, -1, false, { line })
end

--- @param num number 1-based line index
function Buffer:line(num)
  if num < 0 then num = self:line_count() + num + 1 end
  return Range.from_line(self.bufnr, num)
end

--- @param start number 1-based line index
--- @param stop number 1-based line index
function Buffer:lines(start, stop) return Range.from_lines(self.bufnr, start, stop) end

--- @param txt_obj string
--- @param opts? { contains_cursor?: boolean; pos?: u.Pos }
function Buffer:txtobj(txt_obj, opts)
  opts = vim.tbl_extend('force', opts or {}, { bufnr = self.bufnr })
  return Range.from_txtobj(txt_obj, opts)
end

--- @param event string|string[]
--- @diagnostic disable-next-line: undefined-doc-name
--- @param opts vim.api.keyset.create_autocmd
function Buffer:autocmd(event, opts)
  vim.api.nvim_create_autocmd(event, vim.tbl_extend('force', opts, { buffer = self.bufnr }))
end

--- @param tree Tree
function Buffer:render(tree) return self.renderer:render(tree) end

return Buffer