{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Language.Haskell.Brittany.Internal.ToBriDoc.Stmt where

import qualified Data.Text as Text
import GHC (GenLocated(L))
import GHC.Hs
import Language.Haskell.Brittany.Internal.Config.Types
import Language.Haskell.Brittany.Internal.ToBriDocTools
import Language.Haskell.Brittany.Internal.Prelude
import Language.Haskell.Brittany.Internal.Types
import Language.Haskell.Brittany.Internal.Components.BriDoc



layoutStmt :: GuardLStmt GhcPs -> ToBriDocM BriDocNumbered
layoutStmt lstmt@(L _ stmt) = do
  indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack
  indentAmount :: Int <-
    mAsk <&> _conf_layout .> _lconfig_indentAmount .> confUnpack
  docFlushCommsPost True lstmt $ case stmt of
    LastStmt NoExtField body Nothing _ -> do
      -- at least the "|" of a monadcomprehension for _some_ reason
      -- is connected to the _body_ of the "result" stmt. So we need
      -- to docHandleListElemComms here..
      docHandleListElemComms (callLayouter layout_expr) body
    BindStmt epAnn lPat expr -> docHandleComms epAnn $ do
      patDoc <- fmap return
        $ callLayouter layout_colsWrapPat =<< callLayouter layout_pat lPat
      expDoc <- shareDoc $ callLayouter layout_expr expr
      docAlt
        [ docCols
          ColBindStmt
          [ appSep patDoc
          , docSeq
            [ appSep $ docLit $ Text.pack "<-"
            , docAddBaseY BrIndentRegular $ docForceParSpacing expDoc
            ]
          ]
        , docCols
          ColBindStmt
          [ appSep patDoc
          , docAddBaseY BrIndentRegular
            $ docPar (docLit $ Text.pack "<-") (expDoc)
          ]
        ]
    LetStmt epAnn binds -> docHandleComms epAnn $ do
      let spanLet = obtainAnnPos epAnn AnnLet
      letDoc <- shareDoc $ docFlushCommsPost True spanLet $ docLitS "let"
      let isFree = indentPolicy == IndentPolicyFree
      let indentFourPlus = indentAmount >= 4
      (wrapBinds, bindrDocsMay) <- callLayouter layout_localBinds binds
      case bindrDocsMay of
        Nothing -> letDoc
          -- i just tested the above, and it is indeed allowed. heh.
        Just (_, []) -> letDoc -- this probably never happens
        Just (_, [bindDoc]) -> docAlt
          [ -- let bind = expr
            docCols
            ColDoLet
            [ appSep $ letDoc
            , let
                f = case indentPolicy of
                  IndentPolicyFree -> docSetBaseAndIndent
                  IndentPolicyLeft -> docForceSingleline
                  IndentPolicyMultiple
                    | indentFourPlus -> docSetBaseAndIndent
                    | otherwise -> docForceSingleline
              in wrapBinds $ f $ return bindDoc
            ]
          , -- let
            --   bind = expr
            docAddBaseY BrIndentRegular $ docPar
            letDoc
            (wrapBinds $ docSetBaseAndIndent $ return bindDoc)
          ]
        Just (_, bindDocs) -> runFilteredAlternative $ do
          -- let aaa = expra
          --     bbb = exprb
          --     ccc = exprc
          addAlternativeCond (isFree || indentFourPlus) $ docSeq
            [ appSep $ letDoc
            , let
                f = if indentFourPlus
                  then docEnsureIndent BrIndentRegular
                  else docSetBaseAndIndent
              in wrapBinds $ f $ docLines $ return <$> bindDocs
            ]
          -- let
          --   aaa = expra
          --   bbb = exprb
          --   ccc = exprc
          addAlternativeCond (not indentFourPlus)
            $ docAddBaseY BrIndentRegular
            $ docPar
                letDoc
                (wrapBinds $ docSetBaseAndIndent $ docLines $ return <$> bindDocs)
    RecStmt epAnn (L _ stmts) _ _ _ _ _ ->
      docHandleComms epAnn $ runFilteredAlternative $ do
        -- rec stmt1
        --     stmt2
        --     stmt3
        addAlternativeCond (indentPolicy == IndentPolicyFree) $ docSeq
          [ docLit (Text.pack "rec")
          , docSeparator
          , docSetBaseAndIndent $ docLines $ layoutStmt <$> stmts
          ]
        -- rec
        --   stmt1
        --   stmt2
        --   stmt3
        addAlternative $ docAddBaseY BrIndentRegular $ docPar
          (docLit (Text.pack "rec"))
          (docLines $ layoutStmt <$> stmts)
    BodyStmt NoExtField expr _ _ -> do
      expDoc <- shareDoc $ callLayouter layout_expr expr
      docAddBaseY BrIndentRegular $ expDoc
    _ -> briDocByExactInlineOnly "some unknown statement" lstmt