-- | Defines Drasil generator functions.
module Language.Drasil.Generate (
  -- * Debugging
  dumpTo, dumpEverything,
  -- * Type checking
  typeCheckSI,
  -- * Generator Functions
  gen, genDot, genCode,
  -- * Types (Printing Options)
  DocType(..), DocSpec(DocSpec), Format(TeX, HTML, JSON), DocChoices(DC),
  -- * Constructor
  docChoices) where

import System.IO (hClose, hPutStrLn, openFile, IOMode(WriteMode))
import Text.PrettyPrint.HughesPJ (Doc, render)
import Prelude hiding (id)
import System.Directory (createDirectoryIfMissing, getCurrentDirectory,
  setCurrentDirectory)
import Data.Time.Clock (getCurrentTime, utctDay)
import Data.Time.Calendar (showGregorian)

import Build.Drasil (genMake)
import Language.Drasil
import Drasil.DocLang (mkGraphInfo)
import SysInfo.Drasil (SystemInformation)
import Language.Drasil.Printers (DocType(SRS, Website, Jupyter), Format(TeX, HTML, JSON),
 makeCSS, genHTML, genTeX, genJSON, PrintingInformation, outputDot)
import Language.Drasil.Code (generator, generateCode, Choices(..), CodeSpec(..),
  Lang(..), getSampleData, readWithDataDesc, sampleInputDD,
  unPP, unJP, unCSP, unCPPP, unSP)
import Language.Drasil.Output.Formats(Filename, DocSpec(DocSpec), DocChoices(DC))

import Language.Drasil.TypeCheck
import Language.Drasil.Dump

import GOOL.Drasil (unJC, unPC, unCSC, unCPPC, unSC)

-- | Generate a number of artifacts based on a list of recipes.
gen :: DocSpec -> Document -> PrintingInformation -> IO ()
gen :: DocSpec -> Document -> PrintingInformation -> IO ()
gen DocSpec
ds Document
fn PrintingInformation
sm = PrintingInformation -> DocSpec -> Document -> IO ()
prnt PrintingInformation
sm DocSpec
ds Document
fn -- FIXME: 'prnt' is just 'gen' with the arguments reordered

-- TODO: Include Jupyter into the SRS setup.
-- | Generate the output artifacts (TeX+Makefile, HTML or Notebook).
prnt :: PrintingInformation -> DocSpec -> Document -> IO ()
prnt :: PrintingInformation -> DocSpec -> Document -> IO ()
prnt PrintingInformation
sm (DocSpec (DC DocType
Jupyter [Format]
_) Filename
fn) Document
body =
  do Document
-> PrintingInformation -> Filename -> DocType -> Format -> IO ()
prntDoc Document
body PrintingInformation
sm Filename
fn DocType
Jupyter Format
JSON
prnt PrintingInformation
sm (DocSpec (DC DocType
dtype [Format]
fmts) Filename
fn) Document
body =
  do forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Document
-> PrintingInformation -> Filename -> DocType -> Format -> IO ()
prntDoc Document
body PrintingInformation
sm Filename
fn DocType
dtype) [Format]
fmts

-- | Helper for writing the documents (TeX / HTML / JSON) to file.
prntDoc :: Document -> PrintingInformation -> String -> DocType -> Format -> IO ()
prntDoc :: Document
-> PrintingInformation -> Filename -> DocType -> Format -> IO ()
prntDoc Document
d PrintingInformation
pinfo Filename
fn DocType
Jupyter Format
_ = DocType
-> Filename
-> Filename
-> Format
-> Document
-> PrintingInformation
-> IO ()
prntDoc' DocType
Jupyter Filename
"Jupyter" Filename
fn Format
JSON Document
d PrintingInformation
pinfo
prntDoc Document
d PrintingInformation
pinfo Filename
fn DocType
dtype Format
fmt =
  case Format
fmt of
    Format
HTML -> do DocType
-> Filename
-> Filename
-> Format
-> Document
-> PrintingInformation
-> IO ()
prntDoc' DocType
dtype (forall a. Show a => a -> Filename
show DocType
dtype forall a. [a] -> [a] -> [a]
++ Filename
"/HTML") Filename
fn Format
HTML Document
d PrintingInformation
pinfo
               DocType -> Filename -> Document -> IO ()
prntCSS DocType
dtype Filename
fn Document
d
    Format
TeX -> do DocType
-> Filename
-> Filename
-> Format
-> Document
-> PrintingInformation
-> IO ()
prntDoc' DocType
dtype (forall a. Show a => a -> Filename
show DocType
dtype forall a. [a] -> [a] -> [a]
++ Filename
"/PDF") Filename
fn Format
TeX Document
d PrintingInformation
pinfo
              DocSpec -> IO ()
prntMake forall a b. (a -> b) -> a -> b
$ DocChoices -> Filename -> DocSpec
DocSpec (DocType -> [Format] -> DocChoices
DC DocType
dtype []) Filename
fn
    Format
JSON -> do DocType
-> Filename
-> Filename
-> Format
-> Document
-> PrintingInformation
-> IO ()
prntDoc' DocType
dtype (forall a. Show a => a -> Filename
show DocType
dtype forall a. [a] -> [a] -> [a]
++ Filename
"/JSON") Filename
fn Format
JSON Document
d PrintingInformation
pinfo
    Format
_ -> forall a. Monoid a => a
mempty

-- | Helper that takes the document type, directory name, document name, format of documents,
-- document information and printing information. Then generates the document file.
prntDoc' :: DocType -> String -> String -> Format -> Document -> PrintingInformation -> IO ()
prntDoc' :: DocType
-> Filename
-> Filename
-> Format
-> Document
-> PrintingInformation
-> IO ()
prntDoc' DocType
dt Filename
dt' Filename
fn Format
format Document
body' PrintingInformation
sm = do
  Bool -> Filename -> IO ()
createDirectoryIfMissing Bool
True Filename
dt'
  Handle
outh <- Filename -> IOMode -> IO Handle
openFile (Filename
dt' forall a. [a] -> [a] -> [a]
++ Filename
"/" forall a. [a] -> [a] -> [a]
++ Filename
fn forall a. [a] -> [a] -> [a]
++ Format -> Filename
getExt Format
format) IOMode
WriteMode
  Handle -> Filename -> IO ()
hPutStrLn Handle
outh forall a b. (a -> b) -> a -> b
$ Doc -> Filename
render forall a b. (a -> b) -> a -> b
$ PrintingInformation
-> DocType -> Format -> Filename -> Document -> Doc
writeDoc PrintingInformation
sm DocType
dt Format
format Filename
fn Document
body'
  Handle -> IO ()
hClose Handle
outh
  where getExt :: Format -> Filename
getExt Format
TeX  = Filename
".tex"
        getExt Format
HTML = Filename
".html"
        getExt Format
JSON = Filename
".ipynb"
        getExt Format
_    = forall a. HasCallStack => Filename -> a
error Filename
"We can only write in TeX, HTML and Jupyter Notebook (for now)."

-- | Helper for writing the Makefile(s).
prntMake :: DocSpec -> IO ()
prntMake :: DocSpec -> IO ()
prntMake ds :: DocSpec
ds@(DocSpec (DC DocType
dt [Format]
_) Filename
_) =
  do Handle
outh <- Filename -> IOMode -> IO Handle
openFile (forall a. Show a => a -> Filename
show DocType
dt forall a. [a] -> [a] -> [a]
++ Filename
"/PDF/Makefile") IOMode
WriteMode
     Handle -> Filename -> IO ()
hPutStrLn Handle
outh forall a b. (a -> b) -> a -> b
$ Doc -> Filename
render forall a b. (a -> b) -> a -> b
$ forall c. RuleTransformer c => [c] -> Doc
genMake [DocSpec
ds]
     Handle -> IO ()
hClose Handle
outh

-- | Helper that creates a CSS file to accompany an HTML file.
-- Takes in the folder name, generated file name, and the document.
prntCSS :: DocType -> String -> Document -> IO ()
prntCSS :: DocType -> Filename -> Document -> IO ()
prntCSS DocType
docType Filename
fn Document
body = do
  Handle
outh2 <- Filename -> IOMode -> IO Handle
openFile (forall a. Show a => a -> Filename
getFD DocType
docType forall a. [a] -> [a] -> [a]
++ Filename
fn forall a. [a] -> [a] -> [a]
++ Filename
".css") IOMode
WriteMode
  Handle -> Filename -> IO ()
hPutStrLn Handle
outh2 forall a b. (a -> b) -> a -> b
$ Doc -> Filename
render (Document -> Doc
makeCSS Document
body)
  Handle -> IO ()
hClose Handle
outh2
  where
    getFD :: a -> Filename
getFD a
dtype = forall a. Show a => a -> Filename
show a
dtype forall a. [a] -> [a] -> [a]
++ Filename
"/HTML/"

-- | Renders the documents.
writeDoc :: PrintingInformation -> DocType -> Format -> Filename -> Document -> Doc
writeDoc :: PrintingInformation
-> DocType -> Format -> Filename -> Document -> Doc
writeDoc PrintingInformation
s DocType
_  Format
TeX  Filename
_  Document
doc = Document -> PrintingInformation -> Doc
genTeX Document
doc PrintingInformation
s
writeDoc PrintingInformation
s DocType
_  Format
HTML Filename
fn Document
doc = PrintingInformation -> Filename -> Document -> Doc
genHTML PrintingInformation
s Filename
fn Document
doc
writeDoc PrintingInformation
s DocType
dt Format
JSON Filename
_  Document
doc = PrintingInformation -> DocType -> Document -> Doc
genJSON PrintingInformation
s DocType
dt Document
doc
writeDoc PrintingInformation
_ DocType
_  Format
_    Filename
_  Document
_   = forall a. HasCallStack => Filename -> a
error Filename
"we can only write TeX/HTML/JSON (for now)"

-- | Generates traceability graphs as .dot files.
genDot :: SystemInformation -> IO ()
genDot :: SystemInformation -> IO ()
genDot SystemInformation
si = do
    Filename
workingDir <- IO Filename
getCurrentDirectory
    let gi :: GraphInfo
gi = SystemInformation -> GraphInfo
mkGraphInfo SystemInformation
si
    Filename -> GraphInfo -> IO ()
outputDot Filename
"TraceyGraph" GraphInfo
gi
    Filename -> IO ()
setCurrentDirectory Filename
workingDir

-- | Calls the code generator.
genCode :: Choices -> CodeSpec -> IO ()
genCode :: Choices -> CodeSpec -> IO ()
genCode Choices
chs CodeSpec
spec = do
  Filename
workingDir <- IO Filename
getCurrentDirectory
  UTCTime
time <- IO UTCTime
getCurrentTime
  [Expr]
sampData <- forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall (m :: * -> *) a. Monad m => a -> m a
return []) (\Filename
sd -> Filename -> DataDesc' -> IO [Expr]
readWithDataDesc Filename
sd forall a b. (a -> b) -> a -> b
$ [CodeVarChunk] -> DataDesc'
sampleInputDD
    (CodeSpec -> [CodeVarChunk]
extInputs CodeSpec
spec)) (Choices -> Maybe Filename
getSampleData Choices
chs)
  Bool -> Filename -> IO ()
createDirectoryIfMissing Bool
False Filename
"src"
  Filename -> IO ()
setCurrentDirectory Filename
"src"
  let genLangCode :: Lang -> IO ()
genLangCode Lang
Java = forall {progRepr :: * -> *} {packRepr :: * -> *}.
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
Java forall a. JavaCode a -> a
unJC forall a. JavaProject a -> a
unJP
      genLangCode Lang
Python = forall {progRepr :: * -> *} {packRepr :: * -> *}.
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
Python forall a. PythonCode a -> a
unPC forall a. PythonProject a -> a
unPP
      genLangCode Lang
CSharp = forall {progRepr :: * -> *} {packRepr :: * -> *}.
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
CSharp forall a. CSharpCode a -> a
unCSC forall a. CSharpProject a -> a
unCSP
      genLangCode Lang
Cpp = forall {progRepr :: * -> *} {packRepr :: * -> *}.
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
Cpp forall a. CppCode CppSrcCode CppHdrCode a -> a
unCPPC forall a. CppProject a -> a
unCPPP
      genLangCode Lang
Swift = forall {progRepr :: * -> *} {packRepr :: * -> *}.
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
Swift forall a. SwiftCode a -> a
unSC forall a. SwiftProject a -> a
unSP
      genCall :: Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> IO ()
genCall Lang
lng progRepr (Program progRepr) -> ProgData
unProgRepr packRepr (Package packRepr) -> PackData
unPackRepr = forall (progRepr :: * -> *) (packRepr :: * -> *).
(OOProg progRepr, PackageSym packRepr) =>
Lang
-> (progRepr (Program progRepr) -> ProgData)
-> (packRepr (Package packRepr) -> PackData)
-> DrasilState
-> IO ()
generateCode Lang
lng progRepr (Program progRepr) -> ProgData
unProgRepr
        packRepr (Package packRepr) -> PackData
unPackRepr forall a b. (a -> b) -> a -> b
$ Lang -> Filename -> [Expr] -> Choices -> CodeSpec -> DrasilState
generator Lang
lng (Day -> Filename
showGregorian forall a b. (a -> b) -> a -> b
$ UTCTime -> Day
utctDay UTCTime
time) [Expr]
sampData Choices
chs CodeSpec
spec
  forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ Lang -> IO ()
genLangCode (Choices -> [Lang]
lang Choices
chs)
  Filename -> IO ()
setCurrentDirectory Filename
workingDir

-- | Constructor for users to choose their document options
docChoices :: DocType -> [Format] -> DocChoices
docChoices :: DocType -> [Format] -> DocChoices
docChoices = DocType -> [Format] -> DocChoices
DC