{-# LANGUAGE NoImplicitPrelude #-}

-- TODO92
{-# OPTIONS_GHC -Wno-unused-imports #-}

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

import qualified Data.Semigroup as Semigroup
import qualified Data.Text as Text
import GHC (GenLocated(L), Located, moduleNameString, unLoc)
import GHC.Hs
import GHC.Types.Basic
import GHC.Types.SourceText(SourceText(SourceText, NoSourceText), sl_st)
import GHC.Unit.Types (IsBootInterface(..))
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



prepPkg :: SourceText -> String
prepPkg rawN = case rawN of
  SourceText n -> n
  -- This would be odd to encounter and the
  -- result will most certainly be wrong
  NoSourceText -> ""
prepModName :: LocatedA e -> e
prepModName = unLoc

layoutImport :: LImportDecl GhcPs -> ToBriDocM BriDocNumbered
layoutImport ldecl@(L _ importD) = docHandleComms ldecl $ case importD of
  ImportDecl _ _ (L _ modName) pkg src safe q False mas mllies -> do
    importCol <- mAsk <&> _conf_layout .> _lconfig_importColumn .> confUnpack
    importAsCol <-
      mAsk <&> _conf_layout .> _lconfig_importAsColumn .> confUnpack
    indentPolicy <- mAsk <&> _conf_layout .> _lconfig_indentPolicy .> confUnpack
    layouters <- mAsk
    let
      compact = indentPolicy /= IndentPolicyFree
      modNameT = Text.pack $ moduleNameString modName
      pkgNameT = Text.pack . prepPkg . sl_st <$> pkg
      masT = Text.pack . moduleNameString . prepModName <$> mas
      hiding = maybe False fst mllies
      minQLength = length "import qualified "
      qLengthReal =
        let
          qualifiedPart = if q /= NotQualified then length "qualified " else 0
          safePart = if safe then length "safe " else 0
          pkgPart = maybe 0 ((+ 1) . Text.length) pkgNameT
          srcPart = case src of
            IsBoot -> length "{-# SOURCE #-} "
            NotBoot -> 0
        in length "import " + srcPart + safePart + qualifiedPart + pkgPart
      qLength = max minQLength qLengthReal
      -- Cost in columns of importColumn
      asCost = length "as "
      hidingParenCost = if hiding then length "hiding ( " else length "( "
      nameCost = Text.length modNameT + qLength
      importQualifiers = docSeq
        [ appSep $ docLit $ Text.pack "import"
        , case src of
          IsBoot -> appSep $ docLit $ Text.pack "{-# SOURCE #-}"
          NotBoot -> docEmpty
        , if safe then appSep $ docLit $ Text.pack "safe" else docEmpty
        , if q /= NotQualified
          then appSep $ docLit $ Text.pack "qualified"
          else docEmpty
        , maybe docEmpty (appSep . docLit) pkgNameT
        ]
      indentName =
        if compact then id else docEnsureIndent (BrIndentSpecial qLength)
      modNameD = indentName $ appSep $ docLit modNameT
      hidDocCol = if hiding then importCol - hidingParenCost else importCol - 2
      hidDocColDiff = importCol - 2 - hidDocCol
      hidDoc =
        if hiding then appSep $ docLit $ Text.pack "hiding" else docEmpty
      importHead = docSeq [importQualifiers, modNameD]
      bindingsD = case mllies of
        Nothing -> docEmpty
        Just (_, llies@(L llEpAnn lies)) -> do
          let hasComments = hasAnyCommentsBelow llies
          let posOpen = obtainAnnPos llEpAnn AnnOpenP
          let posClose = obtainAnnPos llEpAnn AnnCloseP
          docOpen <- shareDoc $ docHandleComms posOpen $ docParenLSep
          docClose <- shareDoc $ docHandleComms posClose $ docParenR
          if compact
            then docAlt
              [ docSeq
                [ hidDoc
                , docForceSingleline
                  $ layout_LLIEs layouters True ShouldSortItems llies
                ]
              , let
                  makeParIfHiding = if hiding
                    then docAddBaseY BrIndentRegular . docPar hidDoc
                    else id
                in makeParIfHiding
                  (layout_LLIEs layouters True ShouldSortItems llies)
              ]
            else do
              ieDs <- layout_annAndSepLLIEs layouters ShouldSortItems llies lies
              -- TODO92 docWrapNodeRest llies
              docHandleComms llies $ docEnsureIndent (BrIndentSpecial hidDocCol)
                $ case ieDs of
                  -- ..[hiding].( )
                    [] -> if hasComments
                      then docPar
                        (docSeq
                          [hidDoc, docOpen, docEmpty]
                        )
                        (docEnsureIndent
                          (BrIndentSpecial hidDocColDiff)
                          docClose
                        )
                      else docSeq
                        [hidDoc, docOpen, docSeparator, docClose]
                    -- ..[hiding].( b )
                    [ieD] -> runFilteredAlternative $ do
                      addAlternativeCond (not hasComments)
                        $ docSeq
                            [ hidDoc
                            , docOpen
                            , docForceSingleline ieD
                            , docSeparator
                            , docClose
                            ]
                      addAlternative $ docPar
                        (docSeq [hidDoc, docOpen, docNonBottomSpacing ieD])
                        (docEnsureIndent
                          (BrIndentSpecial hidDocColDiff)
                          docClose
                        )
                    -- ..[hiding].( b
                    --            , b'
                    --            )
                    (ieD : ieDs') -> docPar
                      (docSeq [hidDoc, docSetBaseY $ docSeq [docOpen, ieD]]
                      )
                      (docEnsureIndent (BrIndentSpecial hidDocColDiff)
                      $ docLines
                      $ ieDs'
                      ++ [docClose]
                      )
      makeAsDoc asT =
        docSeq [appSep $ docLit $ Text.pack "as", appSep $ docLit asT]
    if compact
      then
        let asDoc = maybe docEmpty makeAsDoc masT
        in
          docAlt
            [ docForceSingleline $ docSeq [importHead, asDoc, bindingsD]
            , docAddBaseY BrIndentRegular
              $ docPar (docSeq [importHead, asDoc]) bindingsD
            ]
      else case masT of
        Just n -> if enoughRoom
          then docLines [docSeq [importHead, asDoc], bindingsD]
          else docLines [importHead, asDoc, bindingsD]
         where
          enoughRoom = nameCost < importAsCol - asCost
          asDoc = docEnsureIndent (BrIndentSpecial (importAsCol - asCost))
            $ makeAsDoc n
        Nothing -> if enoughRoom
          then docSeq [importHead, bindingsD]
          else docLines [importHead, bindingsD]
          where enoughRoom = nameCost < importCol - hidingParenCost
  _ -> docEmpty
