{-# LANGUAGE DeriveAnyClass    #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
module Dhall.Format
    ( 
      Format(..)
    , FormatMode(..)
    , format
    ) where
import Control.Exception (Exception)
import Data.Monoid ((<>))
import Dhall.Pretty (CharacterSet(..), annToAnsiStyle)
import Dhall.Util (Censor, Input(..), Header(..))
import qualified Data.Text.Prettyprint.Doc                 as Pretty
import qualified Data.Text.Prettyprint.Doc.Render.Terminal as Pretty.Terminal
import qualified Data.Text.Prettyprint.Doc.Render.Text     as Pretty.Text
import qualified Control.Exception
import qualified Data.Text.IO
import qualified Dhall.Pretty
import qualified Dhall.Util
import qualified System.AtomicWrite.Writer.LazyText        as AtomicWrite.LazyText
import qualified System.Console.ANSI
import qualified System.IO
data NotFormatted = NotFormatted
    deriving (Exception)
instance Show NotFormatted where
    show _ = ""
data Format = Format
    { characterSet :: CharacterSet
    , censor       :: Censor
    , formatMode   :: FormatMode
    }
data FormatMode
    = Modify { inplace :: Input }
    | Check  { path :: Input }
format
    :: Format
    -> IO ()
format (Format {..}) = do
    let layoutHeaderAndExpr (Header header, expr) =
            Dhall.Pretty.layout
                (   Pretty.pretty header
                <>  Dhall.Pretty.prettyCharacterSet characterSet expr
                <>  "\n")
    let layoutInput input = do
            headerAndExpr <- Dhall.Util.getExpressionAndHeader censor input
            return (layoutHeaderAndExpr headerAndExpr)
    case formatMode of
        Modify {..} -> do
            docStream <- layoutInput inplace
            case inplace of
                InputFile file -> do
                    AtomicWrite.LazyText.atomicWriteFile
                        file
                        (Pretty.Text.renderLazy docStream)
                StandardInput -> do
                    supportsANSI <- System.Console.ANSI.hSupportsANSI System.IO.stdout
                    Pretty.Terminal.renderIO
                        System.IO.stdout
                        (if supportsANSI
                            then (fmap annToAnsiStyle docStream)
                            else (Pretty.unAnnotateS docStream))
        Check {..} -> do
            originalText <- case path of
                InputFile file -> Data.Text.IO.readFile file
                StandardInput  -> Data.Text.IO.getContents
            docStream <- case path of
                InputFile _    -> layoutInput path
                StandardInput  -> do
                    headerAndExpr <- Dhall.Util.getExpressionAndHeaderFromStdinText censor originalText
                    return (layoutHeaderAndExpr headerAndExpr)
            let formattedText = Pretty.Text.renderStrict docStream
            if originalText == formattedText
                then return ()
                else Control.Exception.throwIO NotFormatted