-- | Markdown file creator for generated GOOL code.
module Language.Drasil.Markdown.CreateMd (
    -- * Main Function
    makeMd,
    -- * Section Creators
    introInfo, whatInfo, verInfo, unsupOS, extLibSec, regularSec, instDoc, endNote) 
    where

import Prelude hiding ((<>))
import Text.PrettyPrint.HughesPJ (Doc, empty, isEmpty, vcat, text, (<+>),
    (<>), punctuate, hsep)
import Language.Drasil.Printing.Helpers (upcase)
import Utils.Drasil

-- | Combines a list of sentences into a final Doc, also appends end note.
makeMd :: [Doc] -> Doc
makeMd :: [Doc] -> Doc
makeMd = [Doc] -> Doc
vcat ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
punctuate Doc
secSep ([Doc] -> [Doc]) -> ([Doc] -> [Doc]) -> [Doc] -> [Doc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> [Doc]
filterEmpty

-- | Example title, authors, and maybe purpose section.
introInfo :: String -> [String] -> Maybe String -> Maybe String -> Doc
introInfo :: String -> [String] -> Maybe String -> Maybe String -> Doc
introInfo String
name [String]
auths Maybe String
motiv Maybe String
descr = Doc -> Doc -> Int -> Doc -> Doc -> Doc
introSec (String -> Doc
text String
name) ([String] -> Doc
listToDoc [String]
auths) ([String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
auths) 
    (String -> Maybe String -> Doc
maybeSub String
"Motivation" Maybe String
motiv) (String -> Maybe String -> Doc
maybeSub String
"Purpose" Maybe String
descr)

-- | Instruction section, contains 4 paragraphs, Running, Building, Input-Output and Config Files.
-- The Config file section is only displayed if there are configuration files.
instDoc :: [String] -> String -> (String, String) -> Doc
instDoc :: [String] -> String -> (String, String) -> Doc
instDoc [String]
cfp String
name (String, String)
inoutn = Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"Making Examples") 
    ((String, String) -> Doc
runInstDoc (String, String)
inoutn Doc -> Doc -> Doc
<> Doc
doubleSep Doc -> Doc -> Doc
<> Doc
makeInstDoc) Doc -> Doc -> Doc
<> String -> (String, String) -> Doc
inOutFile String
name (String, String)
inoutn Doc -> Doc -> Doc
<> [String] -> Doc
configSec [String]
cfp

-- | 'What' section in generated README file, displays description and scope if not empty
whatInfo :: Maybe String -> Maybe String -> Doc
whatInfo :: Maybe String -> Maybe String -> Doc
whatInfo Maybe String
descr Maybe String
sc = Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"What") (String -> Maybe String -> Doc
maybeSub String
"Background" Maybe String
descr Doc -> Doc -> Doc
<> String -> Maybe String -> Doc
maybeSub String
"Scope" Maybe String
sc)

-- | Helper for creating optional Intro subsection as Doc
maybeSub :: String -> Maybe String -> Doc
maybeSub :: String -> Maybe String -> Doc
maybeSub String
role = Doc -> (String -> Doc) -> Maybe String -> Doc
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Doc
empty (\String
content-> Doc
doubleSep Doc -> Doc -> Doc
<> String -> Doc
text (String
"> " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
role String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":") 
  Doc -> Doc -> Doc
<+> String -> Doc
upcase String
content)

-- | Helper for giving instructions on the command line.
commandLine :: Doc
commandLine :: Doc
commandLine = String -> Doc
text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ String
"In your terminal command line, enter the same directory as this " String -> String -> String
forall a. [a] -> [a] -> [a]
++
    String
"README file. Then enter the following line:"

-- | Helper for giving instructions on how to run the program.
runInstDoc :: (String, String) -> Doc
runInstDoc :: (String, String) -> Doc
runInstDoc (String
inFile, String
_) = String -> Doc
text String
"How to Run the Program:" Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<>
    Doc
commandLine Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> Doc
bkQuote3 Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text String
"make run RUNARGS=" Doc -> Doc -> Doc
<> String -> Doc
text String
inFile
      Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> Doc
bkQuote3

-- | Helper for giving instructions on how to build the program.
makeInstDoc :: Doc
makeInstDoc :: Doc
makeInstDoc = String -> Doc
text String
"How to Build the Program:" Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> Doc
commandLine Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<>
    Doc
bkQuote3 Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text String
"make build" Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> Doc
bkQuote3

-- | Helper for giving instuctions and Input and Output files.
-- * This needs a more permanent solution (For cases of no Input/Output file).
inOutFile :: String -> (String, String) -> Doc
inOutFile :: String -> (String, String) -> Doc
inOutFile String
name (String
inFile, String
outFile) = Doc
doubleSep Doc -> Doc -> Doc
<>
      String -> Doc
text String
"How to Change Input:" Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text String
name Doc -> Doc -> Doc
<+> 
      String -> Doc
text String
"will take the inputs from" Doc -> Doc -> Doc
<+> Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
inFile Doc -> Doc -> Doc
<> Doc
bkQuote Doc -> Doc -> Doc
<+> 
      String -> Doc
text String
"and write the outputs to" Doc -> Doc -> Doc
<+> Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
outFile Doc -> Doc -> Doc
<> Doc
bkQuote Doc -> Doc -> Doc
<> 
      String -> Doc
text String
"." Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text String
"Inputs can be changed by editing" Doc -> Doc -> Doc
<+> Doc
bkQuote Doc -> Doc -> Doc
<> 
      String -> Doc
text String
inFile Doc -> Doc -> Doc
<> Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
"."

-- | Helper for giving instructions for configuration files.
configSec :: [String] -> Doc
configSec :: [String] -> Doc
configSec [] = Doc
empty
configSec [String]
cfp = Doc
doubleSep Doc -> Doc -> Doc
<> Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"Configuration Files") 
  (String -> Doc
text (String
"Configuration files are files that must be " String -> String -> String
forall a. [a] -> [a] -> [a]
++
    String
"in the same directory as the executable in order to run or build successfully.")
    Doc -> Doc -> Doc
<> Doc
doubleSep Doc -> Doc -> Doc
<> Doc
bkQuote Doc -> Doc -> Doc
<> [String] -> Doc
listToDoc [String]
cfp Doc -> Doc -> Doc
<> Doc
bkQuote)

-- | Language version section.
verInfo :: String -> String -> Doc
verInfo :: String -> String -> Doc
verInfo String
pl String
plv = Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"Version") (Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
pl Doc -> Doc -> Doc
<+> String -> Doc
text String
plv Doc -> Doc -> Doc
<> Doc
bkQuote)

-- | Invalid Operating Systems section, does not display unless atleast 1 invalid OS.
unsupOS :: Maybe String -> Doc
unsupOS :: Maybe String -> Doc
unsupOS = Doc -> (String -> Doc) -> Maybe String -> Doc
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Doc
empty (\String
uns-> Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"Unsupported Operating Systems")
    (String -> Doc
text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ String
"- " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
uns))

-- | External Libraries section. The inputs are a list of name and version pairs
-- and a list of the corresponding version numbers, these are first combined into a 
-- list of triplets, and then each printed on a new line.
extLibSec:: [(String, String)] -> [String]-> Doc
extLibSec :: [(String, String)] -> [String] -> Doc
extLibSec [(String, String)]
libns [String]
libfps = 
    let libs :: [(String, String, String)]
libs = [(String, String)] -> [String] -> [(String, String, String)]
addListToTuple [(String, String)]
libns [String]
libfps
        formattedLibs :: Doc
formattedLibs = ([Doc] -> Doc
hsep ([Doc] -> Doc)
-> ([(String, String, String)] -> [Doc])
-> [(String, String, String)]
-> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
punctuate Doc
contSep ([Doc] -> [Doc])
-> ([(String, String, String)] -> [Doc])
-> [(String, String, String)]
-> [Doc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> [Doc]
filterEmpty ([Doc] -> [Doc])
-> ([(String, String, String)] -> [Doc])
-> [(String, String, String)]
-> [Doc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. 
            ((String, String, String) -> Doc)
-> [(String, String, String)] -> [Doc]
forall a b. (a -> b) -> [a] -> [b]
map (String, String, String) -> Doc
libStatment) [(String, String, String)]
libs
    in if Doc -> Bool
isEmpty Doc
formattedLibs then Doc
empty else 
            Doc -> Doc -> Doc
regularSec (String -> Doc
text String
"External Libraries") Doc
formattedLibs

-- | Helper for formatting the library section.
libStatment :: (String, String, String) -> Doc
libStatment :: (String, String, String) -> Doc
libStatment (String
"",String
"", String
_) = Doc
empty
libStatment (String
nam,String
vers, String
fp) = Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
nam Doc -> Doc -> Doc
<+>
    String -> Doc
text String
vers Doc -> Doc -> Doc
<> Doc
bkQuote Doc -> Doc -> Doc
<> if String
fp String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"" then Doc
empty else
    String -> Doc
text String
". The local file path to the library is" Doc -> Doc -> Doc
<+> Doc
bkQuote Doc -> Doc -> Doc
<> String -> Doc
text String
fp Doc -> Doc -> Doc
<> Doc
bkQuote

-- | Helper for converting a list of tuples and another list into a list of triplets.
addListToTuple :: [(String,String)] -> [String] -> [(String, String, String)]
addListToTuple :: [(String, String)] -> [String] -> [(String, String, String)]
addListToTuple [] [] = []
addListToTuple ((String
n,String
v):[(String, String)]
_) [] = [(String
n,String
v,String
"")]
addListToTuple ((String
n,String
v):[(String, String)]
xtup) (String
l:[String]
xlst) = (String
n,String
v,String
l)(String, String, String)
-> [(String, String, String)] -> [(String, String, String)]
forall a. a -> [a] -> [a]
:[(String, String)] -> [String] -> [(String, String, String)]
addListToTuple [(String, String)]
xtup [String]
xlst
addListToTuple [(String, String)]
_ [String]
_ = []

-- TODO: Allow licenses to have updated date information.
-- | License section.
license :: Doc -> Doc
license :: Doc -> Doc
license Doc
auth = String -> Doc
text String
"Copyright (c) 2021," Doc -> Doc -> Doc
<+> Doc
auth Doc -> Doc -> Doc
<>
  String -> Doc
text String
". All rights reserved. Please see the [full license](https://github.com/JacquesCarette/Drasil/blob/4b9ad0a3016fecb3c7a2aa82ab142f9e805b5cc8/LICENSE) for more details."

-- | End section.
endNote :: Int -> [String] -> Doc
endNote :: Int -> [String] -> Doc
endNote Int
num [String]
auth = String -> Doc
text String
"*This README is a software artifact generated by Drasil.*" 
  Doc -> Doc -> Doc
<> Doc
doubleSep Doc -> Doc -> Doc
<> Doc -> Doc
license ([String] -> Doc
listToDoc [String]
auth) Doc -> Doc -> Doc
<> Doc
doubleSep Doc -> Doc -> Doc
<>
  Int -> Doc
drasilImage Int
num

-- | Section seperators.
secSep, doubleSep, bkQuote, bkQuote3 :: Separator
-- | Horizontal line separator.
secSep :: Doc
secSep = String -> Doc
text String
"\n\n------------------------------------------------------------"
-- | Double newline separator.
doubleSep :: Doc
doubleSep = String -> Doc
text String
"\n\n"
-- | Back quote separator.
bkQuote :: Doc
bkQuote = String -> Doc
text String
"`"
-- | Triple backquote separator.
bkQuote3 :: Doc
bkQuote3 = String -> Doc
text String
"```"


-- FIXME as explained in #2224 we still need to add in the purpose section, 
-- this could be done by adding a third parameter to introSec
-- | Constructs introduction section from header and message.
introSec ::  Doc -> Doc -> Int -> Doc -> Doc -> Doc
introSec :: Doc -> Doc -> Int -> Doc -> Doc -> Doc
introSec Doc
hd Doc
ms1 Int
l Doc
motiv Doc
purp = String -> Doc
text String
"#" Doc -> Doc -> Doc
<+> Doc
hd Doc -> Doc -> Doc
<+> Doc
contSep Doc -> Doc -> Doc
<> (if Int
l Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 then String -> Doc
text String
"> Author:" else String -> Doc
text String
"> Authors: ") 
  Doc -> Doc -> Doc
<+> Doc
ms1 Doc -> Doc -> Doc
<> Doc
motiv Doc -> Doc -> Doc
<> Doc
purp

-- | Constructs regular section section from header and message.
regularSec :: Doc -> Doc -> Doc
regularSec :: Doc -> Doc -> Doc
regularSec Doc
hd Doc
ms = String -> Doc
text String
"##" Doc -> Doc -> Doc
<+> Doc
hd Doc -> Doc -> Doc
<+> Doc
contSep Doc -> Doc -> Doc
<+> Doc
ms

-- Function to create the prefix for the path of the Drasil Logo
buildPath :: Int -> String
buildPath :: Int -> String
buildPath Int
num = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
' ') (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ [String] -> String
unwords ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Int -> String -> [String]
forall a. Int -> a -> [a]
replicate Int
num String
"../"

-- | Drasil Tree icon. Uses HTML directly to format image since normal markdown doesn't support it.
drasilImage :: Int -> Doc
drasilImage :: Int -> Doc
drasilImage Int
num = String -> Doc
alignImage (Int -> String
buildPath Int
num String -> String -> String
forall a. [a] -> [a] -> [a]
++
  String
"drasil-website/WebInfo/images/Icon.png")

-- | Aligns an image to the center using HTML, since markdown doesn't support it.
alignImage :: FilePath -> Doc
alignImage :: String -> Doc
alignImage String
img = String -> Doc
text String
"<p align=\"center\">" Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text (String
"<img src=\"" 
  String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
img String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\" alt=\"Drasil Tree\" width=\"200\" />") Doc -> Doc -> Doc
<> Doc
contSep Doc -> Doc -> Doc
<> String -> Doc
text String
"</p>"