{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}

{-# LANGUAGE CPP #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MonadComprehensions #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE KindSignatures #-}

module Language.Haskell.Brittany.Internal.LayoutBasics
  ( processDefault
  , layoutByExact
  -- , layoutByExactR
  , descToBlockStart
  , descToBlockMinMax
  , descToMinMax
  , rdrNameToText
  , lrdrNameToText
  , lrdrNameToTextAnn
  , askIndent
  , calcLayoutMin
  , calcLayoutMax
  , getCurRemaining
  , layoutWriteAppend
  , layoutWriteAppendMultiline
  , layoutWriteNewline
  , layoutWriteNewlinePlain
  , layoutWriteEnsureNewline
  , layoutWriteEnsureBlock
  , layoutWriteEnsureBlockPlusN
  , layoutWithAddIndent
  , layoutWithAddIndentBlock
  , layoutWithAddIndentN
  , layoutWithAddIndentNBlock
  , layoutWithNonParamIndent
  , layoutWriteEnsureAbsoluteN
  , layoutAddSepSpace
  , moveToExactAnn
  , moveToExactAnn'
  , setOpIndent
  , stringLayouter
  , layoutWritePriorComments
  , layoutWritePostComments
  , layoutIndentRestorePostComment
  , layoutWritePriorCommentsRestore
  , layoutWritePostCommentsRestore
  , extractCommentsPrior
  , extractCommentsPost
  , applyLayouter
  , applyLayouterRestore
  , filterAnns
  , layouterFToLayouterM
  , ppmMoveToExactLoc
  , customLayouterF
  , docEmpty
  , docLit
  , docAlt
  , docSeq
  , docPar
  -- , docCols
  , docPostComment
  , docWrapNode
  , briDocByExact
  , fromMaybeIdentity
  , foldedAnnKeys
  )
where



-- more imports here..

import qualified Language.Haskell.GHC.ExactPrint as ExactPrint
import qualified Language.Haskell.GHC.ExactPrint.Annotate as ExactPrint.Annotate
import qualified Language.Haskell.GHC.ExactPrint.Types as ExactPrint.Types
import qualified Language.Haskell.GHC.ExactPrint.Utils as ExactPrint.Utils

import Language.Haskell.GHC.ExactPrint.Types ( AnnKey, Annotation )

import qualified Data.Text.Lazy.Builder as Text.Builder

import Language.Haskell.Brittany.Internal.Config.Types
import Language.Haskell.Brittany.Internal.Types
import Language.Haskell.Brittany.Internal.Utils

import           RdrName ( RdrName(..) )
import           GHC ( runGhc, GenLocated(L), moduleNameString )
import qualified Outputable    as GHC
import qualified DynFlags      as GHC
import qualified FastString    as GHC
import qualified SrcLoc        as GHC
import           SrcLoc ( SrcSpan )
import           OccName ( occNameString )
import           Name ( getOccString )
import           Module ( moduleName )
import           ApiAnnotation ( AnnKeywordId(..) )

import           Data.Data
import           Data.Generics.Schemes
import           Data.Generics.Aliases

import           DataTreePrint

import qualified Text.PrettyPrint as PP

import           Data.Function ( fix )



processDefault :: (ExactPrint.Annotate.Annotate ast, MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiReader ExactPrint.Types.Anns m)
               => GenLocated SrcSpan ast
               -> m ()
processDefault x = do
  anns <- mAsk
  let str = ExactPrint.exactPrint x anns
  -- this hack is here so our print-empty-module trick does not add
  -- a newline at the start if there actually is no module header / imports
  -- / anything.
  -- TODO: instead the appropriate annotation could be removed when "cleaning"
  --       the module (header). This would remove the need for this hack!
    --test
  case str of
    "\n" -> return ()
    _    -> mTell $ Text.Builder.fromString $ str


layoutByExact :: ( MonadMultiReader Config m
                 , MonadMultiReader (ExactPrint.Types.Anns) m
                 , ExactPrint.Annotate.Annotate ast
                  )
               => GenLocated SrcSpan ast -> m Layouter
layoutByExact x = do
  anns <- mAsk
  trace (showTreeWithCustom (customLayouterF anns) x) $ layoutByExactR x
  -- trace (ExactPrint.Utils.showAnnData anns 2 x) $ layoutByExactR x

layoutByExactR :: (MonadMultiReader Config m
                  , MonadMultiReader (ExactPrint.Types.Anns) m
                  , ExactPrint.Annotate.Annotate ast)
               => GenLocated SrcSpan ast -> m Layouter
layoutByExactR x = do
  indent <- askIndent
  anns <- mAsk
  let t = Text.pack $ ExactPrint.exactPrint x anns
  let tlines = Text.lines $ t <> Text.pack "\n"
      tlineCount = length tlines
  let len = indent + maximum (Text.length <$> tlines)
  return $ Layouter
    { _layouter_desc = LayoutDesc Nothing $ Just $ BlockDesc AllSameIndent len len Nothing
    , _layouter_func = \_ -> do
        -- layoutWriteEnsureBlock
        layoutWriteAppend $ Text.pack $ "{-" ++ show (ExactPrint.Types.mkAnnKey x, Map.lookup (ExactPrint.Types.mkAnnKey x) anns) ++ "-}"
        zip [1..] tlines `forM_` \(i, l) -> do
          layoutWriteAppend $ l
          unless (i==tlineCount) layoutWriteNewline
        do
          let subKeys = foldedAnnKeys x
          state <- mGet
          let filterF k _ = not $ k `Set.member` subKeys
          mSet $ state
            { _lstate_commentsPrior = Map.filterWithKey filterF
                                    $ _lstate_commentsPrior state
            , _lstate_commentsPost  = Map.filterWithKey filterF
                                    $ _lstate_commentsPost  state
            }
    , _layouter_ast = x
    }

briDocByExact :: (ExactPrint.Annotate.Annotate ast,
                     MonadMultiReader Config m,
                     MonadMultiReader ExactPrint.Types.Anns m
                     ) => GenLocated SrcSpan ast -> m BriDoc
briDocByExact ast = do
  anns <- mAsk
  traceIfDumpConf "ast" _dconf_dump_ast_unknown
    (printTreeWithCustom 100 (customLayouterF anns) ast)
  return $ docExt ast anns

descToBlockStart :: LayoutDesc -> Maybe BlockStart
descToBlockStart (LayoutDesc _ (Just (BlockDesc bs _ _ _))) = Just bs
descToBlockStart (LayoutDesc (Just line) _)                 = Just $ RestOfLine line
descToBlockStart _                                          = Nothing

descToBlockMinMax :: LayoutDesc -> Maybe (Int, Int)
descToBlockMinMax (LayoutDesc _ (Just (BlockDesc _ bmin bmax _))) = Just (bmin, bmax)
descToBlockMinMax _                                               = Nothing

descToMinMax :: Int -> LayoutDesc -> Maybe (Int, Int)
descToMinMax p (LayoutDesc _ (Just (BlockDesc start bmin bmax _))) =
  Just (max rolMin bmin, max rolMin bmax)
    where
      rolMin = case start of
        RestOfLine rol -> p + _lColumns_min rol
        AllSameIndent -> 0

descToMinMax p (LayoutDesc (Just (LayoutColumns _ _ lmin)) _)    =
  Just (len, len)
    where
      len = p + lmin
descToMinMax _ _                                                 =
  Nothing

rdrNameToText :: RdrName -> Text
-- rdrNameToText = Text.pack . show . flip runSDoc unsafeGlobalDynFlags . ppr
rdrNameToText ( Unqual occname     ) = Text.pack $ occNameString occname
rdrNameToText ( Qual mname occname ) = Text.pack $ moduleNameString mname 
                                                ++ "."
                                                ++ occNameString occname
rdrNameToText ( Orig modul occname ) = Text.pack $ moduleNameString (moduleName modul)
                                                ++ occNameString occname
rdrNameToText ( Exact name )         = Text.pack $ getOccString name

lrdrNameToText :: GenLocated l RdrName -> Text
lrdrNameToText (L _ n) = rdrNameToText n

lrdrNameToTextAnn :: ( MonadMultiReader Config m
                     , MonadMultiReader (Map AnnKey Annotation) m
                     )
                  => GenLocated SrcSpan RdrName
                  -> m Text
lrdrNameToTextAnn ast@(L _ n) = do
  anns <- mAsk
  let t = rdrNameToText n
  let hasUni x (ExactPrint.Types.G y, _) = x==y
      hasUni _ _ = False
  -- TODO: in general: we should _always_ process all annotaiton stuff here.
  --       whatever we don't probably should have had some effect on the
  --       output. in such cases, resorting to byExact is probably the safe
  --       choice.
  return $ case Map.lookup (ExactPrint.Types.mkAnnKey ast) anns of
    Nothing -> traceShow "Nothing" t
    Just (ExactPrint.Types.Ann _ _ _ aks _ _) -> if
        | any (hasUni AnnBackquote) aks -> Text.pack "`" <> t <> Text.pack "`"
        | any (hasUni AnnOpenP)     aks -> Text.pack "(" <> t <> Text.pack ")"
        | otherwise -> t


askIndent :: (MonadMultiReader Config m) => m Int
askIndent = runIdentity . _lconfig_indentAmount . _conf_layout <$> mAsk

-- minimum block width, judged from block info or line, whichever is
-- available.
-- example: calcLayoutMin doBlock ~~~ atomically $ do
--                                      foo
--                                    ##              indent
--                                    #############   linepre
--                                    ############### result (in this case)
calcLayoutMin :: Int -- basic indentation amount
              -> Int -- currently used width in current line (after indent)
                     -- used to accurately calc placing of the current-line
                     -- stuff ("do" in the above example) and its width.
              -> LayoutDesc
              -> Int
calcLayoutMin indent linePre (LayoutDesc line block) = case (line, block) of
  (_, Just (BlockDesc AllSameIndent m _ _))    -> indent + m
  (_, Just (BlockDesc (RestOfLine inl) m _ _)) -> max (linePre + _lColumns_min inl) (indent + m)
  (Just s, _)                                  -> indent + _lColumns_min s
  _ -> error "bad LayoutDesc mnasdoiucxvlkjasd"

-- see 
calcLayoutMax :: Int -- basic indentation amount
              -> Int -- currently used width in current line (after indent)
                     -- used to accurately calc placing of the current-line
                     -- stuff ("do" in the above example) and its width.
              -> LayoutDesc
              -> Int
calcLayoutMax indent linePre (LayoutDesc line block) = case (line, block) of
  (Just s, _)                                  -> linePre + _lColumns_min s
  (_, Just (BlockDesc AllSameIndent _ m _))    -> indent + m
  (_, Just (BlockDesc (RestOfLine inl) _ m _)) -> max (linePre + _lColumns_min inl) (indent + m)
  _ -> error "bad LayoutDesc msdnfgouvadnfoiu"

getCurRemaining :: ( MonadMultiReader Config m
                  , MonadMultiState LayoutState m
                  )
                => m Int
getCurRemaining = do
  cols <- mAsk <&> _conf_layout .> _lconfig_cols .> runIdentity
  clc <- _lstate_curLineCols <$> mGet
  return $ cols - clc

layoutWriteAppend :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => Text
                  -> m ()
layoutWriteAppend t = do
  state <- mGet
  if _lstate_addSepSpace state
    then do
      mSet $ state { _lstate_curLineCols = _lstate_curLineCols state + Text.length t + 1
                   , _lstate_addSepSpace = False
                   }
      mTell $ Text.Builder.fromText $ Text.pack " " <> t
    else do
      mSet $ state { _lstate_curLineCols = _lstate_curLineCols state + Text.length t }
      mTell $ Text.Builder.fromText t

layoutWriteAppendMultiline :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => Text
                  -> m ()
layoutWriteAppendMultiline t = case Text.lines t of
  [] -> return ()
  (l:lr) -> do
    layoutWriteAppend l
    lr `forM_` \x -> do
      layoutWriteNewlinePlain
      layoutWriteAppend x

-- adds a newline and adds spaces to reach the current indentation level.
-- TODO: rename newline -> newlineBlock and newlinePlain -> newline
layoutWriteNewline :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => m ()
layoutWriteNewline = do
  state <- mGet
  mSet $ state { _lstate_curLineCols = _lstate_indent state
                          , _lstate_commentCol = Nothing
                          , _lstate_addSepSpace = False
                          }
  mTell $ Text.Builder.fromString $ "\n" ++ replicate (_lstate_indent state) ' '

-- | does _not_ add spaces to again reach the current indentation levels.
layoutWriteNewlinePlain :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => m ()
layoutWriteNewlinePlain = do
  state <- mGet
  mSet $ state { _lstate_curLineCols = 0
                          , _lstate_commentCol = Nothing
                          , _lstate_addSepSpace = False
                          }
  mTell $ Text.Builder.fromString $ "\n"

layoutWriteEnsureNewline :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => m ()
layoutWriteEnsureNewline = do
  state <- mGet
  when (_lstate_curLineCols state /= _lstate_indent state)
    $ layoutWriteNewline

layoutWriteEnsureBlock :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => m ()
layoutWriteEnsureBlock = do
  state <- mGet
  let diff = _lstate_curLineCols state - _lstate_indent state
  if diff>0
    then layoutWriteNewline
    else if diff<0
      then do
        layoutWriteAppend $ Text.pack $ replicate (negate diff) ' '
        mSet $ state { _lstate_curLineCols = _lstate_indent state
                     , _lstate_addSepSpace = False
                     }
      else return ()

layoutWriteEnsureAbsoluteN :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => Int -> m ()
layoutWriteEnsureAbsoluteN n = do
  state <- mGet
  let diff = n - _lstate_curLineCols state
  if diff>0
    then do
      layoutWriteAppend $ Text.pack $ replicate diff ' '
      mSet $ state { _lstate_curLineCols = n
                   , _lstate_addSepSpace = False
                   }
    else return ()

layoutWriteEnsureBlockPlusN :: (MonadMultiWriter
                                                           Text.Builder.Builder m,
                                                         MonadMultiState LayoutState m)
                            => Int -> m ()
layoutWriteEnsureBlockPlusN n = do
  state <- mGet
  let diff = _lstate_curLineCols state - _lstate_indent state - n
  if diff>0
    then layoutWriteNewline
    else if diff<0
      then do
        layoutWriteAppend $ Text.pack $ replicate (negate diff) ' '
        mSet $ state { _lstate_addSepSpace = False }
      else return ()

layoutWithAddIndent :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m
                                               ,MonadMultiReader Config m)
                  => m ()
                  -> m ()
layoutWithAddIndent m = do
  amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> runIdentity
  state <- mGet
  mSet state { _lstate_indent = _lstate_indent state + amount }
  m
  do
    s <- mGet
    mSet $ s { _lstate_indent = _lstate_indent state }

layoutWithAddIndentBlock :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m
                                               ,MonadMultiReader Config m)
                  => m ()
                  -> m ()
layoutWithAddIndentBlock m = do
  amount <- mAsk <&> _conf_layout .> _lconfig_indentAmount .> runIdentity
  state <- mGet
  mSet state { _lstate_indent = _lstate_indent state + amount }
  layoutWriteEnsureBlock
  m
  do
    s <- mGet
    mSet $ s { _lstate_indent = _lstate_indent state }

layoutWithAddIndentNBlock :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => Int
                  -> m ()
                  -> m ()
layoutWithAddIndentNBlock amount m = do
  state <- mGet
  mSet state { _lstate_indent = _lstate_indent state + amount }
  layoutWriteEnsureBlock
  m
  do
    s <- mGet
    mSet $ s { _lstate_indent = _lstate_indent state }

layoutWithAddIndentN :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                  => Int
                  -> m ()
                  -> m ()
layoutWithAddIndentN amount m = do
  state <- mGet
  mSet state { _lstate_indent = _lstate_indent state + amount }
  m
  do
    s <- mGet
    mSet $ s { _lstate_indent = _lstate_indent state }

layoutAddSepSpace :: MonadMultiState LayoutState m => m ()
layoutAddSepSpace = do
  state <- mGet
  mSet $ state { _lstate_addSepSpace = True }

moveToExactAnn :: (Data.Data.Data x,
                    MonadMultiWriter Text.Builder.Builder m,
                    MonadMultiState LayoutState m,
                    MonadMultiReader (Map AnnKey Annotation) m) => GenLocated SrcSpan x -> m ()
moveToExactAnn ast = do
  anns <- mAsk
  case Map.lookup (ExactPrint.Types.mkAnnKey ast) anns of
    Nothing -> return ()
    Just ann -> do
      let ExactPrint.Types.DP (x, _y) = ExactPrint.Types.annEntryDelta ann
      replicateM_ x $ layoutWriteNewline

-- TODO: when refactoring is complete, the other version of this method
-- can probably be removed.
moveToExactAnn' :: (MonadMultiWriter Text.Builder.Builder m,
                    MonadMultiState LayoutState m,
                    MonadMultiReader (Map AnnKey Annotation) m) => AnnKey -> m ()
moveToExactAnn' annKey = do
  anns <- mAsk
  case Map.lookup annKey anns of
    Nothing -> return ()
    Just ann -> do
      -- curY <- mGet <&> _lstate_curLineCols
      let ExactPrint.Types.DP (x, _y) = ExactPrint.Types.annEntryDelta ann
      replicateM_ x $ layoutWriteNewline
      -- when (x/=0) $ do
      --   replicateM_ x $ layoutWriteNewlinePlain
      --   mModify $ \s -> s { _lstate_curLineCols = curY }
      --   mTell $ Text.Builder.fromString $ replicate curY ' '

ppmMoveToExactLoc :: MonadMultiWriter Text.Builder.Builder m
                  => ExactPrint.Types.DeltaPos
                  -> m ()
ppmMoveToExactLoc (ExactPrint.Types.DP (x,y)) = do
  replicateM_ x $ mTell $ Text.Builder.fromString "\n"
  replicateM_ y $ mTell $ Text.Builder.fromString " "

layoutWithNonParamIndent :: (MonadMultiWriter
                                                 Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
    => LayoutFuncParams -> m () -> m ()
layoutWithNonParamIndent params m = do
  case _params_opIndent params of
    Nothing -> m
    Just x -> layoutWithAddIndentN x m

setOpIndent :: Int -> LayoutDesc -> LayoutFuncParams -> LayoutFuncParams
setOpIndent i desc p = p
  { _params_opIndent = Just $ case _bdesc_opIndentFloatUp =<< _ldesc_block desc of
      Nothing -> i
      Just j -> max i j
  }

stringLayouter :: Data.Data.Data ast
               => GenLocated SrcSpan ast -> Text -> Layouter
stringLayouter ast t = Layouter
  { _layouter_desc = LayoutDesc
    { _ldesc_line = Just $ LayoutColumns
      { _lColumns_key = ColumnKeyUnique
      , _lColumns_lengths = [Text.length t]
      , _lColumns_min = Text.length t
      }
    , _ldesc_block = Nothing
    }
  , _layouter_func = \_ -> do
      layoutWritePriorCommentsRestore ast
      layoutWriteAppend t
      layoutWritePostComments ast
  , _layouter_ast = ast
  }

layoutWritePriorComments :: (Data.Data.Data ast,
                                               MonadMultiWriter Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                         => GenLocated SrcSpan ast -> m ()
layoutWritePriorComments ast = do
  mAnn <- do
    state <- mGet
    let key = ExactPrint.Types.mkAnnKey ast
    let m   = _lstate_commentsPrior state
    let mAnn = Map.lookup key m
    mSet $ state { _lstate_commentsPrior = Map.delete key m }
    return mAnn
  case mAnn of
    Nothing -> return ()
    Just priors -> do
      when (not $ null priors) $ do
        state <- mGet
        mSet $ state { _lstate_commentCol = Just $ _lstate_curLineCols state }
      priors `forM_` \( ExactPrint.Types.Comment comment _ _
                      , ExactPrint.Types.DP (x, y)
                      ) -> do
        replicateM_ x layoutWriteNewlinePlain
        layoutWriteAppend $ Text.pack $ replicate y ' '
        layoutWriteAppendMultiline $ Text.pack $ comment

-- this currently only extracs from the `annsDP` field of Annotations.
-- per documentation, this seems sufficient, as the
-- "..`annFollowingComments` are only added by AST transformations ..".
layoutWritePostComments :: (Data.Data.Data ast,
                                               MonadMultiWriter Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                         => GenLocated SrcSpan ast -> m ()
layoutWritePostComments ast = do
  mAnn <- do
    state <- mGet
    let key = ExactPrint.Types.mkAnnKey ast
    let m   = _lstate_commentsPost state
    let mAnn = Map.lookup key m
    mSet $ state { _lstate_commentsPost = Map.delete key m }
    return mAnn
  case mAnn of
    Nothing -> return ()
    Just posts -> do
      when (not $ null posts) $ do
        state <- mGet
        mSet $ state { _lstate_commentCol = Just $ _lstate_curLineCols state }
      posts `forM_` \( ExactPrint.Types.Comment comment _ _
                      , ExactPrint.Types.DP (x, y)
                      ) -> do
        replicateM_ x layoutWriteNewlinePlain
        layoutWriteAppend $ Text.pack $ replicate y ' '
        layoutWriteAppendMultiline $ Text.pack $ comment

layoutIndentRestorePostComment :: ( Monad m
           , MonadMultiState LayoutState m
           , MonadMultiWriter Text.Builder.Builder m
           )
                               => m ()
layoutIndentRestorePostComment = do
  mCommentCol <- _lstate_commentCol <$> mGet
  case mCommentCol of
    Nothing -> return ()
    Just commentCol -> do
      layoutWriteNewlinePlain
      layoutWriteAppend $ Text.pack $ replicate commentCol ' '

layoutWritePriorCommentsRestore :: (Data.Data.Data ast,
                                               MonadMultiWriter Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                                => GenLocated SrcSpan ast -> m ()
layoutWritePriorCommentsRestore x = do
  layoutWritePriorComments x
  layoutIndentRestorePostComment

layoutWritePostCommentsRestore :: (Data.Data.Data ast,
                                               MonadMultiWriter Text.Builder.Builder m,
                                               MonadMultiState LayoutState m)
                                => GenLocated SrcSpan ast -> m ()
layoutWritePostCommentsRestore x = do
  layoutWritePostComments x
  layoutIndentRestorePostComment

extractCommentsPrior :: ExactPrint.Types.Anns -> PriorMap
extractCommentsPrior anns = flip Map.mapMaybe anns $ \ann ->
  [r | let r = ExactPrint.Types.annPriorComments ann, not (null r)]
extractCommentsPost  :: ExactPrint.Types.Anns -> PostMap
extractCommentsPost  anns = flip Map.mapMaybe anns $ \ann ->
  [r
  | let
    r = ExactPrint.Types.annsDP ann
      >>= \case
                 (ExactPrint.Types.AnnComment comment, dp) -> [(comment, dp)]
                 _ -> []
  , not (null r)
  ]


applyLayouter :: Layouter -> LayoutFuncParams -> LayoutM ()
applyLayouter l@(Layouter _ _ ast) params = do
  -- (always) write the prior comments at this point.
  layoutWritePriorCommentsRestore ast
  -- run the real stuff.
  _layouter_func l params
  -- if the _layouter_func has not done so already at some point
  -- (there are nodes for which this makes sense),
  -- write the post comments.
  -- effect is `return ()` if there are no postComments.
  layoutWritePostComments ast

applyLayouterRestore :: Layouter -> LayoutFuncParams -> LayoutM ()
applyLayouterRestore l@(Layouter _ _ ast) params = do
  -- (always) write the prior comments at this point.
  layoutWritePriorCommentsRestore ast
  -- run the real stuff.
  _layouter_func l params
  -- if the _layouter_func has not done so already at some point
  -- (there are nodes for which this makes sense),
  -- write the post comments.
  -- effect is `return ()` if there are no postComments.
  layoutWritePostCommentsRestore ast

foldedAnnKeys :: Data.Data.Data ast
              => ast
              -> Set ExactPrint.Types.AnnKey
foldedAnnKeys ast = everything
  Set.union
  (\x -> maybe
         Set.empty
         Set.singleton
         [ gmapQi 1 (\t -> ExactPrint.Types.mkAnnKey $ L l t) x
         | typeRepTyCon (typeOf (L () ())) == (typeRepTyCon (typeOf x))
         , l <- gmapQi 0 cast x
         ]
  )
  ast

filterAnns :: Data.Data.Data ast
           => ast
           -> ExactPrint.Types.Anns
           -> ExactPrint.Types.Anns
filterAnns ast anns =
  Map.filterWithKey (\k _ -> k `Set.member` foldedAnnKeys ast) anns

layouterFToLayouterM :: MultiReader '[Config, ExactPrint.Types.Anns] a -> LayoutM a
layouterFToLayouterM m = do
  settings <- mAsk
  anns <- mAsk
  return $ runIdentity
         $ runMultiReaderTNil
         $ Control.Monad.Trans.MultiReader.Lazy.withMultiReader anns
         $ Control.Monad.Trans.MultiReader.Lazy.withMultiReader settings
         $ m

-- new BriDoc stuff

docEmpty :: BriDoc
docEmpty = BDEmpty

docLit :: Text -> BriDoc
docLit t = BDLit t

docExt :: ExactPrint.Annotate.Annotate ast
       => GenLocated SrcSpan ast -> ExactPrint.Types.Anns -> BriDoc
docExt x anns = BDExternal
                  (ExactPrint.Types.mkAnnKey x)
                  (foldedAnnKeys x)
                  (Text.pack $ ExactPrint.exactPrint x anns)

docAlt :: [BriDoc] -> BriDoc
docAlt = BDAlt


docSeq :: [BriDoc] -> BriDoc
docSeq = BDSeq


docPostComment :: Data.Data.Data ast
               => GenLocated SrcSpan ast
               -> BriDoc
               -> BriDoc
docPostComment ast bd = BDAnnotationPost (ExactPrint.Types.mkAnnKey ast) bd

docWrapNode :: Data.Data.Data ast
            => GenLocated SrcSpan ast
            -> BriDoc
            -> BriDoc
docWrapNode ast bd = BDAnnotationPrior (ExactPrint.Types.mkAnnKey ast)
                   $ BDAnnotationPost (ExactPrint.Types.mkAnnKey ast)
                   $ bd

docPar :: BriDoc
       -> BriDoc
       -> BriDoc
docPar line indented = BDPar BrIndentNone line indented

-- docPar :: BriDoc
--        -> BrIndent
--        -> [BriDoc]
--        -> BriDoc
-- docPar = BDPar

-- docCols :: ColSig
--         -> [BriDoc]
--         -> BriDoc
-- docCols = BDCols


fromMaybeIdentity :: Identity a -> Maybe a -> Identity a
fromMaybeIdentity x y = Data.Coerce.coerce
                      $ fromMaybe (Data.Coerce.coerce x) y