local lspconf = require('lspconfig')

local on_attach = function(client, bufnr)
    vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')

    local function buf_set_keymap(...)
        vim.api.nvim_buf_set_keymap(bufnr, ...)
    end

    local opts = {noremap = true, silent = true}
    buf_set_keymap('n', 'gD', '<Cmd>lua vim.lsp.buf.declaration()<CR>', opts)
    buf_set_keymap('n', 'gd', '<Cmd>lua vim.lsp.buf.definition()<CR>', opts)
    buf_set_keymap('n', 'K', '<Cmd>lua vim.lsp.buf.hover()<CR>', opts)
    buf_set_keymap('n', 'gi', '<Cmd>lua vim.lsp.buf.implementation()<CR>', opts)
    buf_set_keymap('n', '<leader>ls', '<Cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
    buf_set_keymap('n', '<leader>la', '<Cmd>lua vim.lsp.buf.add_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<leader>lr', '<Cmd>lua vim.lsp.buf.remove_workspace_folder()<CR>', opts)
    buf_set_keymap('n', '<leader>lw', '<Cmd>lua print(vim.inspect(vim.lsp.buf.list_workspace_folders()))<CR>', opts)
    buf_set_keymap('n', '<leader>ld', '<Cmd>lua vim.lsp.buf.type_definition()<CR>', opts)
    buf_set_keymap('n', '<leader>ln', '<Cmd>lua vim.lsp.buf.rename()<CR>', opts)
    buf_set_keymap('n', 'gr', '<Cmd>lua vim.lsp.buf.references()<CR>', opts)
    buf_set_keymap('n', '<leader>le', '<Cmd>lua vim.lsp.diagnostic.show_line_diagnostics()<CR>', opts)
    buf_set_keymap('n', '[d', '<Cmd>lua vim.lsp.diagnostic.goto_prev()<CR>', opts)
    buf_set_keymap('n', ']d', '<Cmd>lua vim.lsp.diagnostic.goto_next()<CR>', opts)
    buf_set_keymap('n', '<leader>ll', '<Cmd>lua vim.lsp.diagnostic.set_loclist()<CR>', opts)

    if client.resolved_capabilities.document_formatting then
        buf_set_keymap('n', '<leader>lo', '<Cmd>lua vim.lsp.buf.formatting()<CR>', opts)
    elseif client.resolved_capabilities.document_range_formatting then
        buf_set_keymap('n', '<leader>lo', '<Cmd>lua vim.lsp.buf.range_formatting({},{0,0},{vim.fn.line("$"),0})<CR>', opts)
    end
end

local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true

---------------------------
-- Server configurations --
---------------------------
-- https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md

-- C/C++
lspconf.clangd.setup {
    on_attach = on_attach,
    capabilities = capabilities,
    filetypes = {'c', 'cpp'},
    cmd = {
        'clangd',
        '-j=2',
        '--background-index',
        '--clang-tidy',
        '--completion-style=detailed',
        '--pch-storage=memory',
        '--header-insertion=iwyu',
        '--header-insertion=decorators'
    }
}

-- Lua
local runtime_path = vim.split(package.path, ';')
table.insert(runtime_path, 'lua/?.lua')
table.insert(runtime_path, 'lua/?/init.lua')

lspconf.sumneko_lua.setup {
    on_attach = on_attach,
    capabilities = capabilities,
    cmd = {
        vim.fn.stdpath('data') .. '/lsp/lua-language-server/bin/Linux/lua-language-server',
        '-E',
        vim.fn.stdpath('data') .. '/lsp/lua-language-server/main.lua'
    },
    settings = {
        Lua = {
            diagnostics = {
                globals = {'vim'}
            },
            runtime = {
                version = 'LuaJIT',
                path = runtime_path
            },
            workspace = {
                library = vim.api.nvim_get_runtime_file('', true),
                maxPreload = 100000,
                preloadFileSize = 100000
            },
            telemetry = {
                enable = false
            }
        }
    }
}

-- Go
lspconf.gopls.setup {
    on_attach = on_attach,
    capabilities = capabilities,
    cmd = {'gopls', '--remote=auto'},
    init_options = {
        usePlaceholders = true,
        completeUnimported = true
    }
}

-- Others
local servers = {
    'dockerls',
    'bashls',
    'pyright',
    'tsserver',
    'html',
    'jsonls',
    'cmake',
    'rust_analyzer'
}
for _, server in ipairs(servers) do
    lspconf[server].setup {
        on_attach = on_attach,
        capabilities = capabilities
    }
end

-- Replace the default lsp diagnostic letters with prettier symbols
vim.fn.sign_define('LspDiagnosticsSignError', {text = ''})
vim.fn.sign_define('LspDiagnosticsSignWarning', {text = ''})
vim.fn.sign_define('LspDiagnosticsSignInformation', {text = ''})
vim.fn.sign_define('LspDiagnosticsSignHint', {text = ''})
