-- init.lua for neovim
--
-- Cheatsheet
-- ##########
-- These are useful commands that I often forget:
-- <F2> open FzF for buffers
-- <F4> open FzF for the project
-- <C-]> go to definition (lsp)
-- record commands with `q<register>` and play them back with `@<register>`
-- Visually select text and then g <C-g> to count characters
-- :registers to list all registers in use
-- Use " to copy/paste to/from registers, e.g., "h y <-- copy to register h
--
-- buffers:
-- ~~~~~~~~
-- gb buffer pick (by letter)
-- gp buffer pin
-- gs sort buffers by (relative) directory
-- 
-- folds:
-- ~~~~~
-- z c/o/a close/open/toggle (zC, zO, zA, do it recursively)
-- zM close all folds, zR open all folds
--
-- lspconfig
-- ~~~~~~~~~
-- <C-w>d : show the diagnostic window
--
-- nvim -d
-- ~~~~~~~
-- do, dp take from (left, right)
-- [c, ]c (previous, next) change
-- :%diffget <buffer number> # Get all changes from buffer number (e.g., 2)
--


-- Automatic installation of vim-plug
local data_dir = vim.fn.stdpath('data')
if vim.fn.empty(vim.fn.glob(data_dir .. '/site/autoload/plug.vim')) == 1 then
    vim.cmd('silent !curl -fLo ' ..
        data_dir ..
        '/site/autoload/plug.vim --create-dirs  https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim')
    vim.o.runtimepath = vim.o.runtimepath
    vim.cmd('autocmd VimEnter * PlugInstall --sync | source $MYVIMRC')
end
local config_path = vim.fn.stdpath("config")

-- Enable all plugins
local vim = vim
local Plug = vim.fn['plug#']

vim.call('plug#begin')
    --Plug('lervag/vimtex', {['for'] = ['latex', 'tex', 'bib']})
    Plug('godlygeek/tabular', {['on'] = 'Tab'})
    -- Plug('dkarter/bullets.vim')

    -- Git information
    Plug('lewis6991/gitsigns.nvim')

    -- fzf+telescope for neovim
    Plug('ibhagwan/fzf-lua')

    -- To work with buffers, the host computer must have some nerd-fonts
    -- installed for the icons to show nicely...
    Plug('nvim-tree/nvim-web-devicons')
    Plug('akinsho/bufferline.nvim', {
        ['tag'] = '*',
        -- Behave correctly when using tabs
        ['do'] = 'patch -p1 < ' .. config_path .. '/modbufferline.patch'
    })
    -- The scope plugin separates buffers in each tab, before using it get use to buffers
    -- Plug('tiagovla/scope.nvim')

    -- Tree sitter
    -- To install new languages: TSInstall <language> (tab completion)
    -- now we need the tree-sitter-cli from https://github.com/tree-sitter/tree-sitter/releases (or cargo)
    Plug('nvim-treesitter/nvim-treesitter', {['do'] = ':TSUpdate'})

    -- LSP
    Plug('neovim/nvim-lspconfig')
    Plug('williamboman/mason.nvim', {
        ['do'] = 'git config fetch.pruneTags false'
    })
    Plug('williamboman/mason-lspconfig.nvim', {
        ['do'] = 'git config fetch.pruneTags false',
    })

    -- Completion
    Plug('saghen/blink.cmp', {['tag'] = '*' })

    -- colorscheme
    Plug('rebelot/kanagawa.nvim')
    Plug('EdenEast/nightfox.nvim', {['do'] = ':NightfoxCompile'})
--      Plug('savq/melange-nvim')

    -- show indentation lines
    Plug("lukas-reineke/indent-blankline.nvim")

vim.call('plug#end')

-- Load the shared config with vim
vim.cmd.source('~/.vim/vimrc_shared')
vim.cmd.source('~/.vim/myfiletypes.vim')

-- Enable gitsigns with defaults
require('gitsigns').setup()

-- TreeSitter config for main branch
-- comment -> higlights TODO, FIXME, etc
require('nvim-treesitter').install({"bash", "python", "lua", "comment", "javascript", "pug", "c", "rust"})
vim.api.nvim_create_autocmd('FileType', {
    -- enable highlight and indent with Tree Sitter 
    -- from https://github.com/MeanderingProgrammer/treesitter-modules.nvim?tab=readme-ov-file#implementing-yourself
    -- TODO: incremental highlight 
    group = vim.api.nvim_create_augroup('treesitter.setup', {}),
    callback = function(args)

        local filetype = args.match
        -- check whether we have a parser for the current language, otherwise exit early
        local language = vim.treesitter.language.get_lang(filetype) or filetype
        if not vim.treesitter.language.add(language) then
            return
        end
        -- replicate `fold = { enable = true }`
        vim.wo.foldmethod = 'expr'
        vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'

        -- replicate `highlight = { enable = true }`
        local buf = args.buf
        vim.treesitter.start(buf, language)

        -- replicate `indent = { enable = true }`
        vim.bo[buf].indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()"
    end,
})
-- TODO: probably not needed anymore, left here just in case
-- This fixes the flickering by treesitter, will be fixed by: https://github.com/neovim/neovim/pull/33145
-- vim.g._ts_force_sync_parsing = true

-- ###
-- LSP
-- See: https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md
-- Note: the code below is only compatible with neovim >= 0.11 and mason-lspconfig >= 2
-- mason-lspconfig >=2 automatically enables installed servers
--
require("mason").setup()
local mason_lspconfig = require("mason-lspconfig")
mason_lspconfig.setup({
    ensure_installed = { "pyright", "esbonio", "emmet_language_server", "eslint", "bashls"},
})

-- Specific configurations:
vim.lsp.config['pyright'] = {
    settings = {
        python = {
            analysis = {
                autoImportCompletions = false,
                typeCheckingMode = "off",
                diagnosticSeverityOverrides = {
                    reportGeneralTypeIssues = "warning",
                    reportMissingTypeStubs = "none",
                    reportOptionalCall = "none",
                    reportOptionalMemberAccess = "none",
                    reportOptionalSubscript = "none"
                }
            }
        }
    }
}
vim.lsp.config['bashls'] = {
    settings = {
        bashIde = {
            shellcheckArguments = {
                "--exclude", "SC2086",
            }
        }
    }
}

-- modify slightly the diagnostic floating window
vim.diagnostic.config({
    -- do not update while in insert mode
    update_in_insert = false,
    virtual_text = true, -- To see inline diagnostic
	float = {
		border = "single",
		format = function(diagnostic)
			return string.format(
				"%s (%s) [%s]",
				diagnostic.message,
				diagnostic.source,
				diagnostic.code or diagnostic.user_data.lsp.code
			)
		end,
	},
})

-- FzfLua
local actions = require("fzf-lua").actions
require("fzf-lua").setup{
    actions = {
        files = {
            true,
            -- ["enter"] = actions.file_tabedit, -- let's try the buffer way for a bit
        }
    }
}

-- Bufferline
-- NB: using scope makes deleting buffers dangerous, close buffers instead
vim.api.nvim_create_user_command("Bc", 
    function()
        vim.cmd("bprevious") -- go to previous buffer
        vim.cmd("bdelete #") -- delete the buffer we were at just before
    end, {}
)
vim.opt.termguicolors = true
require("bufferline").setup({
    options = {
        always_show_bufferline = false,
        auto_toggle_bufferline = true, -- this takes over showtabline
        sort_by = "directory",
        --          close_command = "",
        --          right_mouse_command = "Bc",
        diagnostics = "nvim_lsp", -- Diagnostic using LSP, snippet copied from bufferline github
        diagnostics_indicator = function(count, level, diagnostics_dict, context)
            local icon = level:match("error") and " " or " "
            return " " .. icon .. count
        end
    }
})
vim.keymap.set('n', 'gb', '<Cmd>BufferLinePick<CR>')
vim.keymap.set('n', 'gp', '<Cmd>BufferLineTogglePin<CR>')
vim.keymap.set('n', 'gs', '<Cmd>BufferLineSortByRelativeDirectory<CR>')
--  require("scope").setup({}) --for now let's use buffers as god intended


-- Blink
require("blink.cmp").setup({
    keymap = {
        preset = 'enter',
        ['<Tab>'] = {'select_next', 'fallback'},
        ['<S-Tab>'] = {'select_prev', 'fallback'},
        ['<C-c>'] = { 'cancel', 'fallback' },
        ["<C-n>"] = { "show", "show_documentation", "hide_documentation" },
    },
    cmdline = {
        enabled = false
    },
    signature = {
        enabled = true,
        window = { show_documentation = false }
    },
    completion = {
        list = {selection = { preselect = false } },
        menu = {
            auto_show = true,
        },
        documentation = {
            auto_show = true,
            auto_show_delay_ms = 700
        },
    },
    sources = {
        -- see: https://github.com/Saghen/blink.cmp/issues/643
        min_keyword_length = function(ctx)
            return ctx.trigger.kind == "trigger_character" and 0 or 2
        end,
        -- do not autocomplete comments, see https://cmp.saghen.dev/recipes#dynamically-picking-providers-by-treesitter-node-filetype
        default = function(ctx)
            local ok, node = pcall(vim.treesitter.get_node)
            -- check both the node and the parent
            if ok and node then
                if vim.tbl_contains({ 'string', 'comment', 'line_comment', 'block_comment', 'block' }, node:type()) then
                    return {}
                end
                local ok_parent, node_parent = pcall(node:parent())
                if ok_parent and node_parent and vim.tbl_contains({ 'string', 'comment', 'line_comment', 'block_comment', 'block' }, node:type()) then
                    return {}
                end
            end
            return { 'lsp', 'path', 'snippets', 'buffer' }
        end,
    }
})

-- enable indentation lines
require("ibl").setup({
    indent = { char = "▏"},
    scope = {
        char = "▎",
        show_start = false,
        show_end = false
    }
})
--  -- hide the first indent level
local hooks = require "ibl.hooks"
hooks.register(
    hooks.type.WHITESPACE,
    hooks.builtin.hide_first_space_indent_level
)

if os.getenv("SSH_TTY") and os.getenv("SSH_TTY") ~= "" then
    -- otherwise when ssh-in to a mac, it doesn't use osc 52
    vim.g.clipboard = {
      name = 'OSC 52',
      copy = {
        ['+'] = require('vim.ui.clipboard.osc52').copy('+'),
        ['*'] = require('vim.ui.clipboard.osc52').copy('*'),
      },
      paste = {
        ['+'] = require('vim.ui.clipboard.osc52').paste('+'),
        ['*'] = require('vim.ui.clipboard.osc52').paste('*'),
      },
    }
end

-- Folding settings
vim.o.foldenable = true
vim.o.foldlevel = 99
vim.o.foldmethod = "expr"
vim.opt.foldexpr = "v:lua.vim.treesitter.foldexpr()"
vim.opt.foldtext = ""
vim.opt.foldcolumn = "0"
vim.opt.fillchars:append({fold = " "})

--######
-- Theme 
--######
--  local default_colors = require("kanagawa.colors")
--  default_colors.bg = "#000000"
--  require('kanagawa').setup({
--      -- Run :KanagawaCompile after making any changes here
--      compile = true,             -- enable compiling the colorscheme
--      dimInactive = false,         -- dim inactive window `:h hl-NormalNC`
--      terminalColors = true,       -- define vim.g.terminal_color_{0,17}
--      theme = "wave",              -- Load "wave" theme when 'background' option is not set
--      background = {               -- map the value of 'background' option to a theme
--          dark = "wave",           
--          light = "lotus"
--      },
--      colors = {
--          theme = {
--              all = {
--                  ui = {
--                      --bg = "#535353",
--                      bg_gutter=  "none"
--                  }
--              }
--          }
--      }
--  })
require('nightfox').setup({
    -- :NightfoxCompile
    options = {
        -- transparent = true,
        dim_inactive = true,
    },
    palettes = {
        nordfox = {
            bg1     = "#1b1d24"
        }
    },
    groups = {
        nordfox = {
            DiffChange = { bg = "#763325"},
            DiffText = { bg = "#9C402C", fg = "black"},
            DiffAdd = { bg = "green"},
            DiffDelete = { bg = "red"},
            StatusLine = {bg = "#8e8ea3", fg = "white"},
            StatusLineNC = {bg = "#757482", fg = "#c3c1d4"}
        }
    }
})
vim.cmd("colorscheme nordfox")

if os.getenv("VIRTUAL_ENV") then
    vim.g.python3_host_prog = os.getenv("VIRTUAL_ENV") .. "/bin/python3"
end
