{-|
Module      : Logic.Judge.Writer
Description : Producing output.
Copyright   : (c) 2017, 2018 N Steenbergen
License     : GPL-3
Maintainer  : ns@slak.ws
Stability   : experimental

This module contains operations and class instances for writing to files or 
terminals.
-}

{-# LANGUAGE PackageImports #-}
module Logic.Judge.Writer 
    ( Format(LaTeX, Plain)
    , writeHeader
    , writeBody
    , writeFooter
    , write
    , plainprint
    , prettyprint
    ) where

import "base" GHC.IO.Handle (Handle, hIsTerminalDevice)
import "base" GHC.IO.Handle.FD (stdout, stderr)
import "bytestring" Data.ByteString (hPut)
import "terminal-size" System.Console.Terminal.Size (size, width)
import qualified "ansi-wl-pprint" Text.PrettyPrint.ANSI.Leijen as PP
import qualified "utf8-string" Data.ByteString.UTF8 as UTF8

import Logic.Judge.Writer.Plain (Printable, pretty)
import Logic.Judge.Writer.LaTeX (LaTeX, latexHeader, latexFooter, latex)

-- | A data type representing the supported file formats.
data Format 
    = LaTeX 
    | Plain 
    deriving (Show, Read)


-- | Write the header associated with a file format to a file.
writeHeader :: Handle -> Format -> IO ()
writeHeader file format = case format of
    LaTeX -> write file latexHeader
    _ -> return ()


-- | Write an object to a file in the given format.
writeBody :: (LaTeX a, Printable a) => Handle -> Format -> a -> IO ()
writeBody file format = write file . case format of
    LaTeX -> latex
    Plain -> pretty


-- | Write the footer associated with a file format to a file.
writeFooter :: Handle -> Format -> IO ()
writeFooter file format = case format of
    LaTeX -> write file latexFooter
    _ -> return ()


-- | Write a document to some file handle. Automatically chooses `prettyprint`
-- or `plainprint` based on whether we are writing to a terminal or not.
write :: Handle -> PP.Doc -> IO ()
write file doc = do
    terminal <- hIsTerminalDevice file
    if terminal
        then prettyprint file doc
        else plainprint file doc


-- | Print ANSI-colorised document to file handle.
prettyprint :: Handle -> PP.Doc -> IO ()
prettyprint file doc = do
    columns <- maybe 79 width `fmap` size
    PP.displayIO file 
        . (PP.renderPretty 1.0 columns) 
        . (PP.<> PP.line)
        . PP.fill columns
        $ doc


-- | Print UTF-8 encoded, plain document to file handle.
plainprint :: Handle -> PP.Doc -> IO ()
plainprint file doc = hPut file 
    . UTF8.fromString
    . flip PP.displayS "" 
    . (PP.renderPretty 1.0 255) 
    . PP.plain
    . (PP.<> PP.line)
    $ doc