" Show the statusline above the commandline.
set laststatus=2

" Define color variables.
let g:statusline#light_green  = {'fg': ['235', '#080808'], 'bg': [ '35', '#0087ff']}
let g:statusline#light_blue   = {'fg': ['235', '#080808'], 'bg': [ '33', '#0087ff']}
let g:statusline#light_orange = {'fg': ['235', '#080808'], 'bg': ['209', '#eb754d']}
let g:statusline#light_red    = {'fg': ['235', '#080808'], 'bg': ['124', '#af0000']}
let g:statusline#light_grey   = {'fg': ['250', "#bcbcbc"], 'bg': ['236', "#303030"]}
let g:statusline#light_violet = {'fg': ['235', '#080808'], 'bg': [ '99', '#986fec']}
let g:statusline#dark_white   = {'fg': [ '15', '#ffffff'], 'bg': ['233', '#121212']}
let g:statusline#dark_yellow  = {'fg': ['179', '#dfaf5f'], 'bg': ['233', '#121212']}

" Create highlight groups.
function! s:hi(group, color) abort
  execute 'highlight '.a:group
        \.' ctermfg='.a:color['fg'][0].' ctermbg='.a:color['bg'][0]
        \.' guifg='.a:color['fg'][1].' guibg='.a:color['fg'][1]
endfunction

" StatusLineLight is shows the mode and cursor information, it is dynamically
" changed by statusline#mode(), give it a default.
call s:hi('StatusLineLight', g:statusline#light_grey)
" StatusLineDusk is shows additional information which is not always present,
" give it a muted color.
call s:hi('StatusLineDusk', g:statusline#light_grey)
" StatusLineDark shows the filename and filetype and takes up most of the
" statusline, give it a dark background.
call s:hi('StatusLineDark', g:statusline#dark_white)
" StatusLineChange show changes in the file by changing the colour of the
" filename, give if a dark background.
call s:hi('StatusLineChange', g:statusline#dark_yellow)

" Construct a statusline for special buffer types.
function! statusline#special(group, name, title)
  " Display current mode with dynamic highlights.
  let l:mode = '%#'.a:group.'# '.a:name.' '
  " Display filename with dark highlights.
  let l:file = '%#StatusLineDark# '.a:title
  " Display current/total lines and column with dynamic highlights.
  let l:line = '%#'.a:group.'# ☰ %l/%L ㏑%2c '
  " Combine the elements into a single string to be evaluated.
  return l:mode.l:file.'%='.l:line
endfunction

" Construct a statusline for generic buffer types.
function! statusline#generic(group, mode)
  " Display current mode with dynamic highlights.
  let l:mode = '%#'.a:group.'# '.a:mode.' '
  " Display spell or paste if set with dusk highlights in a group to swallow
  " the spaces when empty.
  let l:edit = '%#StatusLineDusk#%( '
        \.'%{&spell ? "Spell " : ""}'
        \.'%{&paste ? "Paste " : ""}'
        \.'%)'
  " Display filename with dark or changed highlights.
  let l:file = (&modified ? '%#StatusLineChange#' : '%#StatusLineDark#').' %f'
  " Display readonly and nomodifiable if set.
  let l:state = '%#StatusLineDark#'
        \.'%{&readonly ? " 🔒" : ""}'
        \.'%{&modifiable ? "" : " ⛔"}'
  " Display filetype if set.
  let l:type = '%#StatusLineDark# %{&filetype} '
  " Display fileencoding if not utf-8 and fileformat if not unix with dusk
  " highlights in a group to swallow spaces when empty.
  let l:format = '%#StatusLineDusk#%( '
        \.'%{&fileencoding ==# "utf-8" ? "" : &fileencoding}'
        \.'%{&fileformat ==# "unix" ? "" : "[".&fileformat."]"}'
        \.' %)'
  " Display current/total lines and column with dynamic highlights.
  let l:line = '%#'.a:group.'# ☰ %l/%L ㏑%2c '
  " Combine the elements into a single string to be evaluated.
  return l:mode.l:edit.l:file.l:state.'%='.l:type.l:format.l:line
endfunction

" Define active statusline, this statusline is dynamic with StatusLineLight
" being updated based on the current mode and only used for current buffer.
function! statusline#active()
  let l:mode = statusline#mode()
  if &buftype ==# 'help'
    if l:mode ==# 'Normal'
      let l:mode = 'Help'
    endif
    return statusline#special('StatusLineLight', l:mode, '%F')
  elseif &buftype ==# 'quickfix'
    " Quickfix list and location list have the same buftype, the window has a
    " loclist flag, query the window info.
    let l:info = getwininfo(win_getid())[0]
    if l:mode ==# 'Normal'
      let l:mode = l:info['loclist'] ? 'Location' : 'Quickfix'
    endif
    return statusline#special('StatusLineLight', l:mode,
          \ get(l:info['variables'], 'quickfix_title', ''))
  elseif &buftype ==# 'terminal'
    return statusline#special('StatusLineLight', 'Terminal', '%f')
  elseif &previewwindow
    if l:mode ==# 'Normal'
      let l:mode = 'Preview'
    endif
    return statusline#generic('StatusLineLight', l:mode)
  endif
  return statusline#generic('StatusLineLight', l:mode)
endfunction

" Define inactive statusline, this remains static until the buffer gains
" focus again.
function! statusline#inactive()
  if &buftype ==# 'help'
    let l:statusline = statusline#special('StatusLineDusk', 'Help', '%F')
  elseif &buftype ==# 'quickfix'
    " Quickfix list and location list have the same buftype, the window has a
    " loclist flag, query the window info.
    let l:info = getwininfo(win_getid())[0]
    let l:statusline = statusline#special('StatusLineDusk',
          \ l:info['loclist'] ? 'Location' : 'Quickfix',
          \ get(l:info['variables'], 'quickfix_title', ''))
  elseif &buftype ==# 'terminal'
    let l:statusline = statusline#special('StatusLineDusk', 'Terminal', '%f')
  elseif &previewwindow
    let l:statusline = statusline#generic('StatusLineDusk', 'Preview')
  else
    let l:statusline = statusline#generic('StatusLineDusk', 'Idle')
  endif
  " Escape spaces and double quotes for use in setlocal.
  let l:statusline = substitute(l:statusline, '\([ "]\)', '\\\0', 'g')
  execute 'setlocal statusline='.l:statusline
endfunction

" Get statusline mode and update StatusLineLight.
function! statusline#mode()
  " Map modes to a human readable name and a color.
  let l:mode = {
  \   'n':  ['Normal',   g:statusline#light_green],
  \   'i':  ['Insert',   g:statusline#light_blue],
  \   'c':  ['Command',  g:statusline#light_green],
  \   'v':  ['Visual',   g:statusline#light_orange],
  \   'V':  ['V-Line',   g:statusline#light_orange],
  \   '': ['V-Block',  g:statusline#light_orange],
  \   'R':  ['Replace',  g:statusline#light_red],
  \   's':  ['Select',   g:statusline#light_violet],
  \   'S':  ['S-Line',   g:statusline#light_violet],
  \   '': ['S-Block',  g:statusline#light_violet],
  \   't':  ['Terminal', g:statusline#light_grey],
  \   '!':  ['Shell',    g:statusline#light_grey],
  \}[mode()]
  " Update the StatusLineLight color.
  call s:hi('StatusLineLight', l:mode[1])
  return l:mode[0]
endfunction

" Setup autocmds to set the statusline per buffer.
augroup benieStatusLine
  autocmd!
  " Dynamically update the current buffer mode and color changes using %! to
  " call a function which is always evaluated on statusline update.
  autocmd BufEnter,WinEnter,BufWinEnter * setlocal statusline=%!statusline#active()
  " Statically set the statusline when leaving the buffer.
  autocmd BufLeave,WinLeave * call statusline#inactive()
augroup END