local map = vim.keymap.set
local is_available = require("core.util").is_available

if is_available("which-key") then
	local prefix = require("which-key").add
	prefix({
		{ "<leader>v", group = "vim" },
		{ "<leader>s", group = "show" },
		{ "<localleader>Z", group = "spelling" },
		{ "]o", group = "options" },
		{ "[o", group = "options" },
	})
end

-- The general ideas behind these mappings:
--
-- * Leader prefix is the generally preferred way to map new things, however
--   only for those that affect all of vim or work in a supra-buffer way.
--
-- * Localleader prefix is used for mappings which only affect single buffers.
--   In other words mostly filetype specific mappings

-- backspace to switch to alternate (last) buffer
map("n", "<BS>", "<C-^>")

-- since u undoes, would it not make sense that U redoes?
map("n", "U", "<C-r>")

-- d-motion puts the last 'deleted' thing into the default register to paste;
-- use D-motion to truly delete something into nothingness and keep whatever
-- you want in your register, ready to paste
map("n", "D", '"_d')

-- I don't particularly need ex mode (at least, yet) but faster macro access is nice
map("n", "Q", "@")

-- stronger versions of left,right - move all the way to beginning/end of line
map("n", "H", "^")
map("n", "L", "$")

-- when in softwrapped files, allow moving through the visible lines with j/k
-- but when prepending a number jump *exactly* as many lines, wrapped or not
-- This makes relative linenumbers much more useful in prose docs since they
-- are always exactly correct
local function wrap_up()
	if vim.v.count == 0 then
		return "gk"
	end
	return "k"
end

local function wrap_down()
	if vim.v.count == 0 then
		return "gj"
	end
	return "j"
end

map("n", "k", wrap_up, { expr = true })
map("n", "j", wrap_down, { expr = true })

-- move around between matching brackets with tab
map("n", "<Tab>", "%")

-- when in insertion mode, C-u uppercases the current word, C-l lowercases it,
map("i", "<C-u>", "<esc>gUiw`]a")
map("i", "<C-y>", "<esc>guiw`]a")

-- Add undo break-points at punctuations for plaintext editing
for _, char in pairs({ ",", ".", ";", "?", "!" }) do
	map("i", char, string.format("%s<c-g>u", char))
end

-- yank current filename/filepath to f buffer
map("n", "yp", ':let @p = expand("%")<Cr>', { desc = "yank filename" })
map("n", "yP", ':let @p = expand("%:p")<Cr>', { desc = "yank filepath" })

-- repeat the last substitute command with all its flags preserved
map("n", "&", ":&&<cr>")

-- bracket pairings to go to the next/previous of:
-- (works with count prefixes)
-- Argument list
map("n", "[a", ":previous<cr>")
map("n", "]a", ":next<cr>")
-- Buffers
map("n", "[b", ":bprevious<cr>")
map("n", "]b", ":bnext<cr>")
-- Quickfix list
map("n", "[q", ":cprevious<cr>")
map("n", "]q", ":cnext<cr>")
-- Location list
map("n", "[l", ":lprevious<cr>")
map("n", "]l", ":lnext<cr>")

-- maps the leader for buffer local mappings
-- since we are (atm) using sneak to go fwd/bwd in fFtT searches, comma does
-- not do too many useful things and can be taken up as localleader
vim.g.maplocalleader = ","

-- If we mapped localleader to comma, we can still get to its original function
-- by douple-tapping it.
-- FIXME does this work still (and is it necessary)?
if vim.g.maplocalleader == "," then
	map("", ",,", ",")
	vim.keymap.del("", ",,", { silent = true })
end

-- get out of terminal mode a little easier by double tapping backslash
map("t", "\\\\", [[<C-\><C-n>]], { desc = "exit terminal mode" })

-- remove search highlights by pressing space+/
map("n", "<leader>/", ":noh<cr>", { desc = "remove highlights" })

-- split buffers vertically/horizontally with the leader \ or - (mirrors my
-- tmux setup)
map("n", "<leader>-", ":sp<cr>", { desc = "open horiz split" })
map("n", "<leader>\\", ":vsp<cr>", { desc = "open vert split" })
-- open actual new tab with leader-T
map("n", "<leader>T", ":tabedit | Vifm<cr>", { desc = "open tab" })

-- select the whole buffer with <leader>-a
map("n", "<leader>a", "ggVG", { desc = "select all" })

-- Format current Paragraph (esp useful in prose writing)
map("n", "<localleader>q", "gqap", { silent = true, desc = "Format current paragraph" })
map("x", "<localleader>q", "gq", { silent = true, desc = "Format {motion}" })
map("n", "<localleader>Q", "vapJgqap", { silent = true, desc = "Unformat then format paragraph" })

-- SPELL CHECKING
-- Move to the prev/next spelling error with [z|[Z ]Z|]z instead of ]s
map("n", "]z", "]s")
map("n", "[z", "[s")
map("n", "]s", "<Nop>")
map("n", "[s", "<Nop>")
map("n", "]Z", "]S")
map("n", "[Z", "[S")
map("n", "]S", "<Nop>")
map("n", "[S", "<Nop>")
-- Move to the prev/next spelling error or suggestion with [z ]z
map("n", "<localleader>ZZ", "<cmd>SpellToggle en_us en_gb de_de<cr>", { desc = "Toggle spellcheck" })
map("n", "<localleader>ZA", "<cmd>SpellToggle! en_us en_gb de_de<cr>", { desc = "Enable spellcheck" })
map("n", "<localleader>ZE", "<cmd>SpellToggle en_us<cr>", { desc = "Toggle EN_US spellcheck" })
map("n", "<localleader>ZB", "<cmd>SpellToggle en_gb<cr>", { desc = "Toggle EN_GB spellcheck" })
map("n", "<localleader>ZD", "<cmd>SpellToggle de_de<cr>", { desc = "Toggle DE_DE spellcheck" })
-- undo last spelling mistake from insert and normal mode
map("i", "<c-z>", "<C-G>u<Esc>e[s1z=`]a<C-G>u")
map("n", "<localleader>z", "mse[s1z=`s", { desc = "Fix last spell error" })
