{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}

{-|
Module      : Text.PrettyPrint.Final.Rendering.Console
Description : A renderer for colored monospace text on a console
Copyright   : (c) David Darais, David Christiansen, and Weixi Ma 2016-2017
License     : MIT
Maintainer  : david.darais@gmail.com
Stability   : experimental
Portability : Portable

A renderer for colored monospace text on a console.
-}

module Text.PrettyPrint.Final.Rendering.Console (render, dumpDoc) where

import Control.Monad
import Control.Applicative
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State
import Control.Monad.RWS
import Data.List
import Data.String (IsString(..))
import Data.Text (Text)
import qualified Data.Text as T

import System.Console.ANSI

import Text.PrettyPrint.Final

renderChunk :: Chunk Int -> Text
renderChunk (CText t) = t
renderChunk (CSpace w) = T.replicate w " "

renderAtom :: Atom Int -> Text
renderAtom (AChunk c) = renderChunk c
renderAtom ANewline = "\n"

-- | Render a 'POut' in some monad.
render :: forall m ann . Monad m
       => (ann -> m () -> m ()) -- ^ How to transform a rendering based on an annotation
       -> (Text -> m ())        -- ^ How to output an atomic string
       -> POut Int ann          -- ^ The document to render
       -> m ()
render renderAnnotation str out = render' out
  where render' :: POut Int ann -> m ()
        render' pout = case pout of
          PNull      -> str ""
          PAtom a    -> str $ renderAtom a
          PSeq o1 o2 -> do render' o1
                           render' o2
          PAnn a o   -> renderAnnotation a $ render' o

-- | Dump pretty printer output to a console.
--
-- In 'IO' to support rendering colors on Windows.
dumpDoc :: (ann -> [SGR])
        -> (ann -> StateT [ann] IO () -> StateT [ann] IO ())
        -> POut Int ann
        -> IO ()
dumpDoc toSGR renderAnnotation =
  flip evalStateT [] .
  render renderAnnotation (lift . putStr . T.unpack)