{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ConstraintKinds #-}

module Language.Haskell.Brittany.Internal.WriteBriDoc.Types where

import           Language.Haskell.Brittany.Internal.Prelude

import qualified Safe
import           GHC                            ( LEpaComment
                                                , RealSrcLoc
                                                )
import qualified Data.Text.Lazy.Builder        as TextL.Builder

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



data PlannedSpace
  = PlannedNone
  | PlannedSameline Int
  | PlannedNewline Int
  | PlannedDelta Int Int
  deriving (Show)

data LayoutState = LayoutState
  { _lstate_baseYs           :: [Int]
     -- ^ stack of number of current indentation columns
     -- (not number of indentations).
  , _lstate_curY :: Int
             -- ^ Either:
             -- 1) number of chars in the current line.
             -- 2) number of newlines to be inserted before inserting any
             --    non-space elements.
  , _lstate_indLevels        :: [Int]
    -- ^ stack of current indentation levels. set for
    -- any layout-affected elements such as
    -- let/do/case/where elements.
    -- The main purpose of this member is to
    -- properly align comments, as their
    -- annotation positions are relative to the
    -- current layout indentation level.
  , _lstate_indLevelLinger   :: Int -- like a "last" of indLevel. Used for
                                  -- properly treating cases where comments
                                  -- on the first indented element have an
                                  -- annotation offset relative to the last
                                  -- non-indented element, which is confusing.
  , _lstate_plannedSpace     :: PlannedSpace
  -- , _lstate_isNewline     :: NewLineState
  --     -- captures if the layouter currently is in a new line, i.e. if the
  --     -- current line only contains (indentation) spaces.
  -- this is mostly superseeded by curYOrAddNewline, iirc.
  , _lstate_commentNewlines  :: Int -- number of newlines inserted due to
                                   -- move-to-DP at a start of a comment.
                                   -- Necessary because some keyword DPs
                                   -- are relative to the last non-comment
                                   -- entity (for some reason).
                                   -- This is not very strictly reset to 0,
                                   -- so we might in some cases get "artifacts"
                                   -- from previous document elements.
                                   -- But the worst effect at the moment would
                                   -- be that we introduce less newlines on
                                   -- moveToKWDP, which seems harmless enough.
  , _lstate_markerForDelta   :: Maybe RealSrcLoc
  }

lstate_baseY :: LayoutState -> Int
lstate_baseY = Safe.headNote "lstate_baseY" . _lstate_baseYs

lstate_indLevel :: LayoutState -> Int
lstate_indLevel = Safe.headNote "lstate_indLevel" . _lstate_indLevels

-- evil, incomplete Show instance; only for debugging.
instance Show LayoutState where
  show state =
    "LayoutState"
    ++ "{baseYs=" ++ show (_lstate_baseYs state)
    ++ ",curY=" ++ show (_lstate_curY state)
    ++ ",indLevels=" ++ show (_lstate_indLevels state)
    ++ ",indLevelLinger=" ++ show (_lstate_indLevelLinger state)
    ++ ",plannedSpace=" ++ show (_lstate_plannedSpace state)
    ++ ",commentNewlines=" ++ show (_lstate_commentNewlines state)
    ++ "}"


type LayoutConstraints m
  = ( MonadMultiReader Config m
    , MonadMultiWriter TextL.Builder.Builder m
    , MonadMultiWriter (Seq String) m
    , MonadMultiState LayoutState m
    , MonadMultiState CommentCounter m
    , MonadMultiState [GHC.LEpaComment] m
    )
