content: {-# LANGUAGE OverloadedStrings #-}
  
  module Cornelis.InfoWin (closeInfoWindows, showInfoWindow, buildInfoBuffer) where
  
  import           Prelude hiding (Left, Right)
  import           Control.Monad (unless)
  import           Control.Monad.State.Class
  import           Cornelis.Pretty
  import           Cornelis.Types
  import           Cornelis.Utils (withBufferStuff, windowsForBuffer, savingCurrentWindow, visibleBuffers)
  import           Data.Foldable (for_)
  import qualified Data.Map as M
  import qualified Data.Set as S
  import qualified Data.Text as T
  import           Data.Traversable (for)
  import qualified Data.Vector as V
  import           Neovim
  import           Neovim.API.Text
  import           Prettyprinter (layoutPretty, LayoutOptions (LayoutOptions), PageWidth (AvailablePerLine))
  import           Prettyprinter.Render.Text (renderStrict)
  
  
  cornelisWindowVar :: Text
  cornelisWindowVar = "cornelis_window"
  
  getBufferVariableOfWindow :: NvimObject o => Text -> Window -> Neovim env (Maybe o)
  getBufferVariableOfWindow variableName window = do
    window_is_valid window >>= \case
      False -> pure Nothing
      True -> do
        buffer <- window_get_buffer window
        let getVariableValue = Just . fromObjectUnsafe <$> buffer_get_var buffer variableName
        getVariableValue `catchNeovimException` const (pure Nothing)
  
  closeInfoWindowsForUnseenBuffers :: Neovim CornelisEnv ()
  closeInfoWindowsForUnseenBuffers = do
    seen <- S.fromList . fmap snd <$> visibleBuffers
    bufs <- gets cs_buffers
    let known = M.keysSet bufs
        unseen = known S.\\ seen
    for_ unseen $ \b -> do
      for_ (M.lookup b bufs) $ \bs -> do
        ws <- windowsForBuffer $ iw_buffer $ bs_info_win bs
        for_ ws $ flip nvim_win_close True
  
  closeInfoWindows :: Neovim env ()
  closeInfoWindows = do
    ws <- vim_get_windows
    for_ ws $ \w -> getBufferVariableOfWindow cornelisWindowVar w >>= \case
      Just True -> nvim_win_close w True
      _ -> pure ()
  
  
  closeInfoWindowForBuffer :: BufferStuff -> Neovim CornelisEnv ()
  closeInfoWindowForBuffer bs = do
    let InfoBuffer ib = bs_info_win bs
    ws <- windowsForBuffer ib
    for_ ws $ flip nvim_win_close True
  
  
  
  showInfoWindow :: Buffer -> Doc HighlightGroup -> Neovim CornelisEnv ()
  showInfoWindow b doc = withBufferStuff b $ \bs -> do
    let ib = bs_info_win bs
    closeInfoWindowsForUnseenBuffers
  
    vis <- visibleBuffers
    ns <- asks ce_namespace
  
    -- Check if the info win still exists, and if so, just modify it
    found <- fmap or $
      for vis $ \(w, vb) -> do
        case vb == iw_buffer ib of
          False -> pure False
          True -> do
            writeInfoBuffer ns ib doc
            resizeInfoWin w ib
            pure True
  
    -- Otherwise we need to rebuild it
    unless found $ do
      closeInfoWindowForBuffer bs
      writeInfoBuffer ns ib doc
      ws <- windowsForBuffer b
      for_ ws $ buildInfoWindow ib
  
  
  
  buildInfoBuffer :: Neovim env InfoBuffer
  buildInfoBuffer = do
    b <-
      -- not listed in the buffer list, is throwaway
      nvim_create_buf False True
  
    -- Setup things in the buffer
    void $ buffer_set_var b cornelisWindowVar $ ObjectBool True
    nvim_buf_set_option b "modifiable" $ ObjectBool False
    nvim_buf_set_option b "filetype" $ ObjectString "agdainfo"
    pure $ InfoBuffer b
  
  
  
  
  buildInfoWindow :: InfoBuffer -> Window -> Neovim CornelisEnv Window
  buildInfoWindow (InfoBuffer split_buf) w = savingCurrentWindow $ do
    nvim_set_current_win w
    max_height <-  asks $ T.pack . show . cc_max_height . ce_config
    max_width <- asks $ T.pack . show . cc_max_width . ce_config
    asks (cc_split_location . ce_config) >>= \case
      Vertical   -> vim_command $ max_width <> " vsplit"
      Horizontal -> vim_command $ max_height <> " split"
      OnLeft     -> vim_command $ "topleft " <> max_width <> " vsplit"
      OnRight    -> vim_command $ "botright " <> max_width <> " vsplit"
      OnTop      -> vim_command $ "topleft " <> max_height <> " split"
      OnBottom   -> vim_command $ "botright " <> max_height <> " split"
    split_win <- nvim_get_current_win
    nvim_win_set_buf split_win split_buf
  
    -- Setup things in the window
    nvim_win_set_option split_win "relativenumber" $ ObjectBool False
    nvim_win_set_option split_win "number" $ ObjectBool False
  
    resizeInfoWin split_win (InfoBuffer split_buf)
  
    pure split_win
  
  resizeInfoWin :: Window -> InfoBuffer -> Neovim CornelisEnv ()
  resizeInfoWin w ib = do
    t <- nvim_buf_get_lines (iw_buffer ib) 0 (-1) False
    max_size <- asks $ cc_max_height . ce_config
    let size = min max_size $ fromIntegral $ V.length t
    asks (cc_split_location . ce_config) >>= \case
      Vertical   -> pure ()
      Horizontal -> window_set_height w size
      OnLeft     -> pure ()
      OnRight    -> pure ()
      OnTop      -> window_set_height w size
      OnBottom   -> window_set_height w size
  
  
  
  writeInfoBuffer :: Int64 -> InfoBuffer -> Doc HighlightGroup -> Neovim env ()
  writeInfoBuffer ns iw doc = do
    -- TODO(sandy): Bad choice for a window, but good enough?
    w <- nvim_get_current_win
    width <- window_get_width w
  
    let sds = layoutPretty (LayoutOptions (AvailablePerLine (fromIntegral width) 0.8)) doc
        (hls, sds') = renderWithHlGroups sds
        s = T.lines $ renderStrict sds'
  
    let b = iw_buffer iw
    nvim_buf_set_option b "modifiable" $ ObjectBool True
    buffer_set_lines b 0 (-1) True $ V.fromList s
  
    for_ (concatMap spanInfoHighlights hls) $ \(InfoHighlight (l, sc) ec hg) ->
      nvim_buf_add_highlight
        b ns
        (T.pack $ show hg)
        l sc ec
    nvim_buf_set_option b "modifiable" $ ObjectBool False
  
  