-- mod-version:3
local core = require "core"
local config = require "core.config"
local common = require "core.common"
local style = require "core.style"
local DocView = require "core.docview"

config.plugins.motiontrail = common.merge({
  enabled = true,
  steps = 50,
  -- The config specification used by the settings gui
  config_spec = {
    name = "Motion Trail",
    {
      label = "Enabled",
      description = "Disable or enable the caret motion trail effect.",
      path = "enabled",
      type = "toggle",
      default = true
    },
    {
      label = "Steps",
      description = "Amount of trail steps to generate on caret movement.",
      path = "steps",
      type = "number",
      default = 50,
      min = 10,
      max = 100
    },
  }
}, config.plugins.motiontrail)

local cc_installed = pcall(require, 'plugins.custom_caret')
local cc_conf = config.plugins.custom_caret

local function get_caret_size(dv, i)
  local line, col = dv.doc:get_selection_idx(i)
  local chw = dv:get_font():get_width(dv.doc:get_char(line, col))
  local w = style.caret_width
  local h = dv:get_line_height()

  if cc_installed then
    local cc_shape = cc_conf.shape
    if cc_shape == "underline" or dv.doc.overwrite then
      w = chw
      h = style.caret_width * 2
    elseif cc_shape ==  "block" then
      w = chw
    end
  end

  return w, h
end

local caret_idx, caret_amt = 1, 0

local dv_update = DocView.update
function DocView:update()
  self.last_pos = self.last_pos or {}
  self.last_view = self.last_view or {}
  self.last_doc_pos = self.last_doc_pos or {}
  caret_idx = caret_idx or 1

  -- continue from whatever caret_idx left
  caret_amt = caret_amt and math.max(caret_amt, caret_idx) or 0
  for i=1, caret_amt - caret_idx do
    local ri = caret_idx + i
    self.last_pos[ri] = nil
    self.last_view[ri] = nil
    self.last_doc_pos[ri] = nil
  end
  caret_idx = 1
  dv_update(self)
end

local dv_draw = DocView.draw
function DocView:draw()
  self.draws = self.draws and self.draws + 1 or 1
  return dv_draw(self)
end

local dv_draw_caret = DocView.draw_caret
function DocView:draw_caret(x, y)
  if not config.plugins.motiontrail.enabled or self ~= core.active_view then
    dv_draw_caret(self, x, y)
    return
  end

  self.last_pos[caret_idx] = self.last_pos[caret_idx] or {}
  self.last_doc_pos[caret_idx] = self.last_doc_pos[caret_idx] or {}
  local line, col = self.doc:get_selection_idx(caret_idx)

  if (self.draws or 0) <= 1 then
    local lsx, lsy = self.last_pos[caret_idx][1] or x, self.last_pos[caret_idx][2] or y
    local lsl, lsc = self.last_doc_pos[caret_idx][1], self.last_doc_pos[caret_idx][2]
    local w, h = get_caret_size(self, caret_idx)

    if self.difference_in_coords and lsx == x and lsy == y then
      self.difference_in_coords = false
    end

    if lsl ~= line or lsc ~= col then self.difference_in_coords = true end

    if self.difference_in_coords and self.last_view[caret_idx] == self then
      local lx = x
      for i = 0, 1, 1 / config.plugins.motiontrail.steps do
        local ix = common.lerp(x, lsx, i)
        local iy = common.lerp(y, lsy, i)
        if cc_installed and cc_conf.shape == "underline" or self.doc.overwrite then
          iy = iy + self:get_line_height()
        end
        local iw = math.max(w, math.ceil(math.abs(ix - lx)))
        local color = style.caret
        if cc_installed and cc_conf.custom_color then
          color = cc_conf.caret_color
        end
        renderer.draw_rect(ix, iy, iw, h, color)
        lx = ix
      end
      core.redraw = true
    end
  end

  self.last_pos[caret_idx][1], self.last_pos[caret_idx][2], self.last_view[caret_idx] = x, y, self
  self.last_doc_pos[caret_idx][1], self.last_doc_pos[caret_idx][2] = line, col
  caret_idx = caret_idx + 1
  self.draws = 0
  dv_draw_caret(self, x, y)
end
