local remap       = vim.keymap.set
local expr_opts   = { noremap=true, unique=true, expr=true, replace_keycodes=false }
local silent_opts = { noremap=true, unique=true, silent=true }
local opts        = { noremap=true, unique=true }

remap('c', '<Esc>', '<C-C>', opts)
remap('n', '<Esc>', '<Esc>:noh<CR>', opts)
-- Disable recording
remap('n', 'q', '<Nop>', { noremap = false })
-- Use Q to execute default register.
remap('n', 'Q', '<Nop>', opts)
-- Copy to clipboard
remap('n', '<Leader>y', ':%y+<CR>', opts)
remap('n', '<Leader>Y', ':%y*<CR>', opts)
remap('v', '<Leader>y', '"+y'     , opts)
remap('v', '<Leader>Y', '"*y'     , opts)
remap('n', '<Leader>p', '<ESC>"+p', opts)
remap('n', '<Leader>P', '<ESC>"*p', opts)
-- Search and Replace
remap('n', 'c.'        , ':%s//g<Left><Left>'                 , opts)
remap('n', '<Leader>c.', ':%s/\\<<C-r><C-w>\\>//g<Left><Left>', opts)
-- Exact Search
remap('n', 'S', '/\\<\\><Left><Left>', opts)
-- Quit
remap('n', '<Leader>x', '<Esc>:x<CR>' , opts)
remap('n', '<Leader>q', '<Esc>:q<CR>' , opts)
remap('n', '<Leader>Q', '<Esc>:qa<CR>', opts)
-- Navigate buffers
remap('n', '[b', ':bprevious<CR>', opts)
remap('n', ']b', ':bnext<CR>'    , opts)
remap('n', '[B', ':bfirst<CR>'   , opts)
remap('n', ']B', ':blast<CR>'    , opts)
-- Reload buffer
remap('n', '<Leader>e', ':e<CR>'    , opts)
remap('n', '<Leader>E', ':bufdo<CR>', opts)
-- Tab navigation
remap('n', '<Leader>tp', ':tabprevious<CR>', opts)
remap('n', '<Leader>tn', ':tabnext<CR>'    , opts)
remap('n', '<Leader>tf', ':tabfirst<CR>'   , opts)
remap('n', '<Leader>tl', ':tablast<CR>'    , opts)
remap('n', '<Leader>tN', ':tabnew<CR>'     , opts)
remap('n', '<Leader>tc', ':tabclose<CR>'   , opts)
-- Jump to first tab & close all other tabs. Helpful after running Git
-- difftool.
remap('n', '<Leader>T',  ':tabfirst<CR>:tabonly<CR>', opts)
-- For tags
remap('n', '[t', ':tprevious<CR>', opts)
remap('n', ']t', ':tNext<CR>'    , opts)
remap('n', '[T', ':tfirst<CR>'   , opts)
remap('n', ']T', ':tlast<CR>'    , opts)
remap('n', '<Leader>ts', ':<C-u>tselect <C-r><C-w><CR>', opts)
-- Quickfix list mappings
remap('n', 'qo', ':copen<CR>'    , opts)
remap('n', 'qc', ':cclose<CR>'   , opts)
remap('n', '[q', ':cprevious<CR>', opts)
remap('n', ']q', ':cnext<CR>'    , opts)
remap('n', '[Q', ':cfirst<CR>'   , opts)
remap('n', ']Q', ':clast<CR>'    , opts)
-- Location list mappings
remap('n', 'Lo', ':lopen<CR>'    , opts)
remap('n', 'Lc', ':lclose<CR>'   , opts)
remap('n', '[l', ':lprevious<CR>', opts)
remap('n', ']l', ':lnext<CR>'    , opts)
remap('n', '[L', ':lfirst<CR>'   , opts)
remap('n', ']L', ':lfirst<CR>'   , opts)
-- Preview tags
remap('n', 'pt', ':ptag <C-R><C-W><CR>'   , opts)
remap('n', '[p', ':ptprevious<CR>'        , opts)
remap('n', ']p', ':ptnext<CR>'            , opts)
remap('n', 'po', ':ppop<CR>'              , opts)
remap('n', 'pc', ':pc<CR>'                , opts)
remap('n', 'pi', ':psearch <C-R><C-W><CR>', opts)
-- Short cuts for setting fold methods
remap('n', 'zmi', ':set foldmethod=indent<CR>', opts)
remap('n', 'zmm', ':set foldmethod=manual<CR>', opts)
remap('n', 'zme', ':set foldmethod=expr<CR>'  , opts)
remap('n', 'zmk', ':set foldmethod=marker<CR>', opts)
remap('n', 'zms', ':set foldmethod=syntax<CR>', opts)

-- Key Bindings to help with terminal mode
remap('t', '<Esc>', '<C-\\><C-n>', opts)

-- Key bindings to move between window splits
remap('n', '<Space>0', '0<C-w>w', opts)
remap('n', '<Space>1', '1<C-w>w', opts)
remap('n', '<Space>2', '2<C-w>w', opts)
remap('n', '<Space>3', '3<C-w>w', opts)
remap('n', '<Space>4', '4<C-w>w', opts)
remap('n', '<Space>5', '5<C-w>w', opts)
remap('n', '<Space>6', '6<C-w>w', opts)
remap('n', '<Space>7', '7<C-w>w', opts)
remap('n', '<Space>8', '8<C-w>w', opts)
remap('n', '<Space>9', '9<C-w>w', opts)

-- Tag helpers
remap('n', '<C-\\>', ':vsp <CR>:<C-u>tag <C-r><C-w><CR>', opts)
remap('n', '<A-]>' , ':sp <CR>:<C-u>tag <C-r><C-w><CR>' , opts)

-- Move across wrapped lines like regular lines
-- Go to the first non-blank character of a line
remap('n', '0', '^', opts)
-- Just in case you need to go to the very beginning of a line
remap('n', '^', '0', opts)

-- Make dot work on visually selected lines
remap('v', '.', ':norm.<CR>', opts)
-- Go to the last file we changed
remap('n', '<BS>', '<C-^>', opts)

-- Use Tab & S-Tab instead of C-g and C-t for incsearch
remap('c', '<Tab>'  , 'getcmdtype() =~ \'[?/]\' ? \'<C-g>\' : feedkeys(\'<Tab>\'  , \'int\')[1]', expr_opts)
remap('c', '<S-Tab>', 'getcmdtype() =~ \'[?/]\' ? \'<C-t>\' : feedkeys(\'<S-Tab>\', \'int\')[1]', expr_opts)

-- Use arrow keys to navigate in popup menu
remap('c', '<Up>'  , 'pumvisible() ? feedkeys(\'<S-Tab>\', \'int\')[1] : \'<Up>\''  , expr_opts)
remap('c', '<Down>', 'pumvisible() ? feedkeys(\'<Tab>\'  , \'int\')[1] : \'<Down>\'', expr_opts)

-- Husk mappings
remap('c', '<C-a>', '<Home>'                        , opts     )
remap('c', '<C-e>', '<End>'                         , opts     )
remap('c', '<C-b>', 'husk#left()'                   , expr_opts)
remap('c', '<C-f>', 'husk#right()'                  , expr_opts)
remap('c', '<C-d>', 'husk#del_word()'               , expr_opts)
remap('c', '<C-k>', 'husk#clear_line_after_cursor()', expr_opts)

-- After shifting a visual block, select it again
remap('v', '<', '<gv', opts)
remap('v', '>', '>gv', opts)

-- Select last pasted/yanked text
remap('n', 'g<C-v>', '`[v`]', opts)

-- Map ;; to :
remap('n', ';;', ':', opts)

-- https://vim.fandom.com/wiki/Moving_lines_up_or_down
remap('n', '<A-j>', ':m .+1<CR>=='       , opts)
remap('n', '<A-k>', ':m .-2<CR>=='       , opts)
remap('i', '<A-j>', '<ESC>:m .+1<CR>==gi', opts)
remap('i', '<A-k>', '<ESC>:m .-2<CR>==gi', opts)
remap('v', '<A-j>', ':m \'>+1<CR>gv=gv'  , opts)
remap('v', '<A-k>', ':m \'<-2<CR>gv=gv'  , opts)

-- Marks
-- '0      - Position of cursor when last exited Vim.
-- '"      - Position of cursor when last exited the current buffer.
-- '.      - Position of last change.
-- '< & '> - Start/end of visual selection.
-- '[ & '] - Start/end of last change or yank.
-- '^      - Position of cursor when last Vim last left insert mode - This is how gi command works.
-- ''      - Position before last jump (Super useful!). See :h ''.
-- Map gm to `. Instead of `a, hit gma. See :help mark-motions as to why we use backtick.
remap('n', 'gm', '`', { noremap = false })
-- Jump using marks to last change, left insert, jump.
remap('n', '<Leader>mc', ':norm `.<CR>' , opts)
remap('n', '<Leader>mi', ':norm `^<CR>' , opts)
remap('n', '<Leader>mj', ':norm `\'<CR>', opts)

remap('n', '[d', '<cmd>lua vim.diagnostic.goto_prev { wrap = false }<CR>' , opts)
remap('n', ']d', '<cmd>lua vim.diagnostic.goto_next { wrap = false }<CR>' , opts)
remap('n', '[D', '<cmd>lua vim.diagnostic.goto_prev()<CR>'                , opts)
remap('n', ']D', '<cmd>lua vim.diagnostic.goto_next()<CR>'                , opts)
remap('n', 'dl', '<cmd>lua vim.diagnostic.setloclist()<CR>'               , opts)
remap('n', 'dq', '<cmd>lua vim.diagnostic.setqflist()<CR>'                , opts)
remap('n', 'dr', '<cmd>lua vim.diagnostic.reset()<CR>'                    , opts)
remap('n', 'dH', '<cmd>lua vim.diagnostic.hide()<CR>'                     , opts)
remap('n', 'dh', '<cmd>lua vim.diagnostic.show()<CR>'                     , opts)
remap('n', ',d', '<cmd>lua vim.diagnostic.open_float(0, {scope="line"})<CR>'  , opts)
remap('n', ',D', '<cmd>lua vim.diagnostic.open_float(0, {scope="cursor"})<CR>', opts)

-- DiffConflicts
remap('n', '<Leader>dc', ':DiffConflicts<CR>'           , opts)
remap('n', '<Leader>ds', ':DiffConflictsShowHistory<CR>', opts)
remap('n', '<Leader>dw', ':DiffConflictsWithHistory<CR>', opts)

-- Tmux pane and nvim window switching
remap('n', '<C-Left>' , ':TmuxNavigateLeft<CR>' , silent_opts)
remap('n', '<C-Right>', ':TmuxNavigateRight<CR>', silent_opts)
remap('n', '<C-Up>'   , ':TmuxNavigateUp<CR>'   , silent_opts)
remap('n', '<C-Down>' , ':TmuxNavigateDown<CR>' , silent_opts)

-- Resize splits
remap('n', '<Leader>+', ':exe "resize " . (winheight(0) * 3/2)<CR>', silent_opts)
remap('n', '<Leader>-', ':exe "resize " . (winheight(0) * 2/3)<CR>', silent_opts)
