local M = {}

function M.cmp_conf()
    local cmp = require('cmp')
    local luasnip = require('luasnip')

    local has_words_before = function()
        local line, col = unpack(vim.api.nvim_win_get_cursor(0))
        return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match('%s') == nil
    end

    cmp.setup {
        sorting = {
            comparators = {
                cmp.config.compare.offset,
                cmp.config.compare.exact,
                cmp.config.compare.score,
                require('cmp-under-comparator').under,
                cmp.config.compare.kind,
                cmp.config.compare.sort_text,
                cmp.config.compare.length,
                cmp.config.compare.order
            }
        },
        formatting = {
            format = function(entry, vim_item)
                local lspkind_icons = {
                    Text = '',
                    Method = '',
                    Function = '',
                    Constructor = '',
                    Field = 'ﰠ',
                    Variable = '',
                    Class = 'ﴯ',
                    Interface = '',
                    Module = '',
                    Property = 'ﰠ',
                    Unit = '塞',
                    Value = '',
                    Enum = '',
                    Keyword = '',
                    Snippet = '',
                    Color = '',
                    File = '',
                    Reference = '',
                    Folder = '',
                    EnumMember = '',
                    Constant = '',
                    Struct = '',
                    Event = '',
                    Operator = '',
                    TypeParameter = ''
                }
                -- load lspkind icons
                vim_item.kind = string.format('%s %s', lspkind_icons[vim_item.kind], vim_item.kind)

                vim_item.menu = ({
                    luasnip = '[SNIP]',
                    path = '[PATH]',
                    buffer = '[BUF]',
                    -- calc = '[CALC]',
                    dictionary = '[DICT]',
                    treesitter = '[TS]',
                    nvim_lsp = '[LSP]',
                    -- cmp_tabnine = '[TN]',
                    latex_symbols = '[TEX]',
                    -- conjure = '[CJ]',
                    orgmode = '[ORG]'
                })[entry.source.name]

                return vim_item
            end
        },
        -- documentation = {
        --     border = {'╭', '─', '╮', '│', '╯', '─', '╰', '│'}
        -- },
        mapping = {
            ['<C-Space>'] = cmp.mapping.complete(),
            ['<C-p>'] = cmp.mapping.select_prev_item(),
            ['<C-n>'] = cmp.mapping.select_next_item(),
            ['<C-b>'] = cmp.mapping.scroll_docs(-4),
            ['<C-f>'] = cmp.mapping.scroll_docs(4),
            ['<C-d>'] = cmp.mapping.close(),
            -- Change choice nodes for luasnip
            ['<C-j>'] = cmp.mapping(function(fallback)
                if luasnip.choice_active() then
                    luasnip.change_choice(-1)
                else
                    fallback()
                end
            end, {'i', 's'}),
            ['<C-k>'] = cmp.mapping(function(fallback)
                if luasnip.choice_active() then
                    luasnip.change_choice(1)
                else
                    fallback()
                end
            end, {'i', 's'}),
            -- supertab-like mapping
            ['<Tab>'] = cmp.mapping(function(fallback)
                if cmp.visible() then
                    cmp.select_next_item()
                elseif luasnip.expand_or_jumpable() then
                    luasnip.expand_or_jump()
                elseif has_words_before() then
                    cmp.complete()
                else
                    fallback()
                end
            end, {'i', 's'}),
            ['<S-Tab>'] = cmp.mapping(function(fallback)
                if cmp.visible() then
                    cmp.select_prev_item()
                elseif luasnip.jumpable(-1) then
                    luasnip.jump(-1)
                else
                    fallback()
                end
            end, {'i', 's'})
        },
        snippet = {
            expand = function(args)
                luasnip.lsp_expand(args.body)
            end
        },
        sources = {
            {name = 'luasnip'},
            {name = 'path'},
            {name = 'buffer'},
            -- {name = 'calc'},
            {name = 'dictionary', keyword_length = 2},
            {name = 'treesitter'},
            {name = 'nvim_lsp'},
            -- {name = 'cmp_tabnine'},
            {name = 'latex_symbols'},
            -- {name = 'conjure'},
            {name = 'orgmode'}
        }
    }

    -- Setup for cmp-dictionary
    -- TODO: do this on FileType * (cmp.setup.buffer) with custom dicts
    -- vim.opt.dictionary = {'/usr/share/dict/words'}
end

-- function M.tabnine_conf()
--     local tabnine = require('cmp_tabnine.config')
--     tabnine:setup {
--         max_lines = 1000,
--         max_num_results = 20,
--         sort = true,
--         run_on_every_keystroke = true
--     }
-- end

function M.autopairs_conf()
    require('nvim-autopairs').setup {
        disable_filetype = {'TelescopePrompt', 'spectre_panel'},
        disable_in_macro = false, -- disable when recording or executing a macro
        ignored_next_char = string.gsub([[ [%w%%%'%[%"%.] ]], '%s+', ''),
        enable_moveright = true,
        enable_afterquote = true, -- add bracket pairs after quote
        enable_check_bracket_line = true, -- check bracket in same line
        check_ts = true, -- use treesitter
        ts_config = {
            lua = {'string', 'source'},
            javascript = {'string', 'template_string'}
        },
        map_bs = true, -- map the <BS> key
        map_c_w = true, -- map <C-w> to delete an pair if possible
        map_cr = false, -- use mapping <CR> for nvim-cmp below instead
        fast_wrap = {
            map = '<A-e>',
            chars = { '{', '[', '(', '"', "'" },
            pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], '%s+', ''),
            offset = 0, -- Offset from pattern match
            end_key = '$',
            keys = 'qwertyuiopzxcvbnmasdfghjkl',
            check_comma = true,
            highlight = 'Search',
            highlight_grey = 'Comment'
        }
    }

    require('nvim-autopairs.completion.cmp').setup {
        map_cr = true, --  map <CR> on insert mode
        map_complete = true, -- it will auto insert `(` after select function or method item
        auto_select = true, -- automatically select the first item
        insert = false, -- use insert confirm behavior instead of replace
        map_char = { -- modifies the function or method delimiter by filetypes
            all = '(',
            tex = '{'
        }
    }
end

function M.snippets_conf()
    local types = require('luasnip.util.types')

    require('luasnip').config.set_config({
        updateevents = 'TextChanged, TextChangedI',
        history = true,
        ext_opts = {
            [types.choiceNode] = {
                active = {
                    virt_text = {{'●', 'LuaSnipChoice'}}
                }
            },
            [types.insertNode] = {
                active = {
                    virt_text = {{'●', 'LuaSnipInsert'}}
                }
            }
        }
    })

    -- Loading vscode-like snippets from 'friendly-snippets'
    require('luasnip/loaders/from_vscode').load()
end

function M.autotag_conf()
    require('nvim-ts-autotag').setup {
        filetypes = {
            'html', 'javascript', 'javascriptreact', 'typescript',
            'typescriptreact', 'svelte', 'vue'
        }
    }
end

return M
