{-# LANGUAGE OverloadedStrings #-}
module Database.Design.Ampersand.Output.PandocAux
      ( writepandoc
      , XRefObj(..) , xRefTo, xRefToLatexRefString
      , headerWithLabel
      , definitionListItemLabel
      , pandocEqnArray
      , pandocEqnArrayWithLabels
      , pandocEqnArrayWithLabel
      , pandocEquation
      , pandocEquationWithLabel
      , count
      , ShowMath(..)
      , latexEscShw, escapeNonAlphaNum
      , xrefCitation
      , texOnly_Id
      , texOnly_fun
      , texOnly_rel
      , newGlossaryEntry
      )
where
import Database.Design.Ampersand.ADL1
import Database.Design.Ampersand.FSpec
import Data.Char hiding    (Space)
import Text.Pandoc
import Text.Pandoc.Builder
import Database.Design.Ampersand.Core.AbstractSyntaxTree
import Database.Design.Ampersand.Basics hiding (hPutStrLn)
import Prelude hiding      (writeFile,readFile,getContents,putStr,putStrLn)
import Database.Design.Ampersand.Misc
import System.Process      (system)
import System.Exit         (ExitCode(ExitSuccess,ExitFailure))
import System.FilePath  -- (combine,addExtension,replaceExtension)
import System.Directory
import System.Info         (os)
import Data.Monoid
import Data.List
import Control.Monad
import Data.Maybe

fatal :: Int -> String -> a
fatal = fatalMsg "Output.PandocAux"

-- | Default key-value pairs for use with the Pandoc template
defaultWriterVariables :: FSpec -> [(String , String)]
defaultWriterVariables fSpec
  = [ ("title", (case (fsLang fSpec, diagnosisOnly (getOpts fSpec)) of
                        (Dutch  , False) -> "Functionele Specificatie van "
                        (English, False) -> "Functional Specification of "
                        (Dutch  ,  True) -> "Diagnose van "
                        (English,  True) -> "Diagnosis of "
                )++name fSpec)
 --   , ("mainfont",
 --   , ("sansfont",
 --   , ("monofont",
 --   , ("mathfont",
    , ("fontsize", "10pt,a4paper")   --can be overridden by geometry package (see below)
    , ("lang"    , case fsLang fSpec of
                       Dutch   -> "dutch"
                       English -> "english")
    , ("mainlang", case fsLang fSpec of
                       Dutch   -> "dutch"
                       English -> "english")
    , ("documentclass","report")
    ] ++
    [ ("toc" , "<<TheTableOfContentsShouldGoHere>>") | not (diagnosisOnly (getOpts fSpec))]++
    [ ("header-includes", unlines
         [ "% ============Ampersand specific Begin================="
         , "% First a couple of LaTeX packages are included:"
         , ""
         , "% The glossaries package supports acronyms and multiple glossaries"
         , "\\usepackage[toc]{glossaries}    % Add the glossaries to the table of contents"
         , "\\makeglossaries"
         , ""
         , "% geometry provides a flexible and easy interface to page dimentions"
         , "\\usepackage[ top=1.5cm, bottom=1.5cm, outer=5cm, inner=2cm"
         , "            , heightrounded, footskip=.5cm"
         , "            , marginparwidth=2.5cm, marginparsep=0.5cm]{geometry}"
         , ""
         , "% breqn – Automatic line breaking of displayed equations"
         , "\\usepackage{breqn}"
         , ""
         , "% colonequals – Colon equals symbols"
         , "\\usepackage{colonequals}"
         , ""
         , ""
         , "\\usepackage{textcomp}"
         , "% == [all]{hypcap} after {hyperref} shows the ref'd picture i.o. the caption @ click =="
         , ""
         , "\\usepackage[all]{hypcap}"
         , ""

         , "% hack1) For the purpose of clear references in Latex. See also https://github.com/AmpersandTarski/ampersand/issues/31"
         , "\\makeatletter"
         , "\\let\\orgdescriptionlabel\\descriptionlabel"
         , "\\renewcommand*{\\descriptionlabel}[1]{%"
         , "  \\let\\orglabel\\label"
         , "  \\let\\label\\@gobble"
         , "  \\phantomsection"
         , "  \\edef\\@currentlabel{#1}%"
         , "  %\\edef\\@currentlabelname{#1}%"
         , "  \\let\\label\\orglabel"
         , "  \\orgdescriptionlabel{#1}%"
         , "}"
         , "\\makeatother"
         , "% End-hack1"
         , ""

         , "% hack2) The LaTeX commands \\[ and \\], are redefined in the amsmath package, making sure that ecuations are"
         , "% not numbered. This is undesireable behaviour. this is fixed with the following hack, inspired on a note"
         , "% found at http://tex.stackexchange.com/questions/40492/what-are-the-differences-between-align-equation-and-displaymath"
         , "\\DeclareRobustCommand{\\[}{\\begin{equation}}"
         , "\\DeclareRobustCommand{\\]}{\\end{equation}}"
         , "% End-hack2"
         , ""
         , ""
         , "\\def\\id#1{\\mbox{\\em #1\\/}}"
         , "\\newcommand{\\marge}[1]{\\marginpar{\\begin{minipage}[t]{3cm}{\\noindent\\small\\em #1}\\end{minipage}}}"
         , "\\def\\define#1{\\label{dfn:#1}\\index{#1}{\\em #1}}"
         , "\\def\\defmar#1{\\label{dfn:#1}\\index{#1}\\marge{#1}{\\em #1}}"
         , "\\newcommand{\\iden}{\\mathbb{I}}"
         , "\\newcommand{\\ident}[1]{\\mathbb{I}_{#1}}"
         , "\\newcommand{\\full}{\\mathbb{V}}"
         , "\\newcommand{\\fullt}[1]{\\mathbb{V}_{[#1]}}"
         , "\\newcommand{\\flip}[1]{{#1}^\\smallsmile} %formerly:  {#1}^\\backsim"
         , "\\newcommand{\\kleeneplus}[1]{{#1}^{+}}"
         , "\\newcommand{\\kleenestar}[1]{{#1}^{*}}"
         , "\\newcommand{\\asterisk}{*}"
         , "\\newcommand{\\cmpl}[1]{\\overline{#1}}"
         , "\\newcommand{\\subs}{\\vdash}"
         , "\\newcommand{\\rel}{\\times}"
         , "\\newcommand{\\fun}{\\rightarrow}"
         , "\\newcommand{\\isa}{\\sqsubseteq}"
         , "\\newcommand{\\N}{\\mbox{\\msb N}}"
         , "\\newcommand{\\disjn}[1]{\\id{disjoint}(#1)}"
         , "\\newcommand{\\fsignat}[3]{\\id{#1}:\\id{#2}\\fun\\id{#3}}"
         , "\\newcommand{\\signat}[3]{\\id{#1}:\\id{#2}\\rel\\id{#3}}"
         , "\\newcommand{\\signt}[2]{\\mbox{\\({#1}_{[{#2}]}\\)}}"
         , "\\newcommand{\\declare}[3]{\\id{#1}:\\ \\id{#2}\\rel\\id{#3}}"
         , "\\newcommand{\\fdeclare}[3]{\\id{#1}:\\ \\id{#2}\\fun\\id{#3}}"
         , "% ============Ampersand specific End==================="
         ])
    | fspecFormat (getOpts fSpec) == FLatex ]

--DESCR -> functions to write the pandoc
--         String = the name of the outputfile
--         The first IO() is a Pandoc output format
--         The second IO(): If the output format is latex, then this IO() generates a .pdf from the .tex
writepandoc :: FSpec -> Pandoc -> (String,IO(),IO())
writepandoc fSpec thePandoc = (outputFile,makeOutput,postProcessMonad)
         where
         outputFile = addExtension (combine (dirOutput (getOpts fSpec)) (baseName (getOpts fSpec)))
                                       (case fspecFormat (getOpts fSpec) of
                                                 Fasciidoc     -> ".txt"
                                                 Fcontext      -> ".context"
                                                 Fdocbook      -> ".docbook"
                                                 Fman          -> ".man"
                                                 Fmarkdown     -> ".md"
                                                 Fmediawiki    -> ".mediawiki"
                                                 Forg          -> ".org"
                                                 Fplain        -> ".plain"
                                                 Frst          -> ".rst"
                                                 FPandoc       -> ".pandoc"
                                                 Frtf          -> ".rtf"
                                                 FLatex        -> ".tex"
                                                 Fhtml         -> ".html"
                                                 Fopendocument -> ".odt"
                                                 Ftexinfo      -> ".texinfo"
                                                 Ftextile      -> ".textile"
                                       )
         makeOutput
            =  do template <- readDefaultTemplate fSpecFormatString
                  verboseLn (getOpts fSpec) ("Generating "++fSpecFormatString++" to : "++outputFile)
                  --verboseLn (getOpts fSpec) "Variables to set in the template:"
                  --verboseLn (getOpts fSpec) (intercalate "\n   " (map show (writerVariables (writerOptions template))))
                  writeFile outputFile (pandocWriter (writerOptions template) thePandoc)
                  verboseLn (getOpts fSpec) "... done."
           where
              pandocWriter :: WriterOptions -> Pandoc -> String
              pandocWriter =
                case fspecFormat (getOpts fSpec) of
                  Fasciidoc -> fatal 145 "No current support for asciidoc"
                  FPandoc   -> writeNative
                  Fcontext  -> writeConTeXt
                  Fdocbook  -> writeDocbook
                  Fhtml     -> writeHtmlString
                  FLatex    -> writeLaTeX
                  Fman      -> writeMan
                  Fmarkdown -> writeMarkdown
                  Fmediawiki -> writeMediaWiki
                  Fopendocument -> writeOpenDocument
                  Forg -> writeOrg
                  Fplain -> writePlain
                  Frst -> writeRST
                  Frtf -> writeRTF
                  Ftexinfo -> writeTexinfo
                  Ftextile -> writeTextile
              fSpecFormatString :: String
              fSpecFormatString =
                case fspecFormat (getOpts fSpec) of
                  FPandoc   -> "pandoc"
                  Fasciidoc -> "asciidoc"
                  Fcontext  -> "context"
                  Fdocbook  -> "docbook"
                  Fhtml     -> "html"
                  FLatex    -> "latex"
                  Fman      -> "man"
                  Fmarkdown -> "markdown"
                  Fmediawiki -> "mediawiki"
                  Fopendocument -> "opendocument"
                  Forg -> "org"
                  Fplain -> "plain"
                  Frst -> "rst"
                  Frtf -> "rtf"
                  Ftexinfo -> "texinfo"
                  Ftextile -> "textile"
              readDefaultTemplate :: String -> IO(Maybe String)
              readDefaultTemplate  s =
                do { let fp = ampersandDataDir (getOpts fSpec) </> ".." </> "outputTemplates" </> "default."++s
                   ; exists <- doesFileExist fp
                   ; (if exists
                      then do verboseLn (getOpts fSpec) $ "Using Template: "++fp
                              contents <- readFile fp
                              return $ Just contents
                      else do putStrLn ""
                              putStrLn  "***WARNING: ***"
                              putStrLn ("Template file does not exist: "++fp)
                              putStrLn  "It was part of the installation of Ampersand."
                              putStrLn  "...trying without template, but that isn't likely going to work..."
                              putStrLn  "    (reinstalling Ampersand should fix this problem...)"
                              putStrLn  "***************"
                              return Nothing
                     )
                   }
              writerOptions :: Maybe String -> WriterOptions
              writerOptions template = case theme (getOpts fSpec) of
                          ProofTheme -> ampersandDefaultWriterOptions
                                           { writerTableOfContents=False
                                           , writerNumberSections=False
                                           }
                          _          -> ampersandDefaultWriterOptions
                     where
                       ampersandDefaultWriterOptions =
                         def
                            { writerStandalone=isJust template
                            , writerTableOfContents=True
                            , writerNumberSections=True
                            , writerTemplate=fromMaybe "" template
                            , writerVariables=defaultWriterVariables fSpec}
         postProcessMonad :: IO()
         postProcessMonad =
           case fspecFormat (getOpts fSpec) of
               FLatex  -> do
                          (ready,nrOfRounds) <- doRestOfPdfLatex (False, 0)  -- initialize with: (<NotReady>, <0 rounds so far>)
                          verboseLn (getOpts fSpec) ("PdfLatex was called "++
                                          (if nrOfRounds>1 then show nrOfRounds++" times" else "once")++
                                          if ready then "."
                                                   else ", but did not solve all references!")
                             where
                                doRestOfPdfLatex :: (Bool,Int) -> IO (Bool,Int)
                                doRestOfPdfLatex (ready, roundsSoFar)
                                  = if ready || roundsSoFar > 4    -- Make sure we will not hit a loop when something is wrong with call to pdfLatex ...
                                    then return (ready, roundsSoFar)
                                    else do result <- callPdfLatexOnce
                                            let logFileName = replaceExtension outputFile ("log"++show roundsSoFar)
                                            renameFile (replaceExtension outputFile "log") logFileName
                                            logFileLines <- fmap lines $ readFile logFileName
                                            {- Old comment: The log file should be renamed before reading, because readFile opens the file
                                               for lazy IO. In a next run, pdfLatex will try to write to the log file again. If it
                                               was read using readFile, it will fail because the file is still open. 8-((
                                            -}
                                            
                                            case result of
                                              ExitSuccess   -> verboseLn (getOpts fSpec) "PDF file created."
                                              ExitFailure _ ->
                                               do { let nrOfErrLines = 15
                                                  ; putStrLn "----------- LaTeX error-----------"
                                                  
                                                  -- get rid of latex memory info and take required nr of lines
                                                  ; let reverseErrLines = take nrOfErrLines . drop 2 
                                                                        . dropWhile (not . ("Here is how much of TeX's memory you used:" `isPrefixOf`)) 
                                                                        . reverse $ logFileLines
                                                  ; putStrLn $ unlines . reverse $ reverseErrLines   
                                                  ; putStrLn "----------------------------------\n"
                                                  ; putStrLn $ "ERROR: Latex execution failed."
                                                  ; putStrLn $ "For more information, run pdflatex on: "++texFilename
                                                  ; putStrLn $ "Or consult the log file:\n"++logFileName
                                                  }

                                            -- We need to rerun latex if any of the log lines start with a rerunPrefix
                                            let notReady = result == ExitSuccess &&
                                                           or [ rerunPrefix `isPrefixOf` line
                                                              | line <- logFileLines
                                                              , rerunPrefix <- [ "LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right."
                                                                               , "Package longtable Warning: Table widths have changed. Rerun LaTeX."
                                                                               ]
                                                              ]
                                            when notReady (verboseLn (getOpts fSpec) "Another round of pdfLatex is required. Hang on...")
                                          --  when notReady (dump "log")  -- Need to dump the last log file, otherwise pdfLatex cannot write its log.
                                            doRestOfPdfLatex (not notReady, roundsSoFar +1)

                                texFilename = addExtension (baseName (getOpts fSpec)) ".tex"

                                callPdfLatexOnce :: IO ExitCode
                                callPdfLatexOnce =
                                   do if os `elem` ["mingw32","mingw64","cygwin","windows"] --REMARK: not a clear enum to check for windows OS
                                      then do { res <- system ( pdfLatexCommand++"> "++combine (dirOutput (getOpts fSpec)) "pdflog" )
                                              ; if res /= ExitSuccess then return res else 
                                                  system  makeIndexCommand -- TODO: failure of makeindex is not reported correctly (requires refactoring command execution)
                                              }
                                      --REMARK: MikTex is windows; Tex-live does not have the flag -include-directory.
                                      else system ( "cd "++dirOutput (getOpts fSpec)++
                                                    " && pdflatex "++commonFlags++
                                                    texFilename ++ "> "++addExtension(baseName (getOpts fSpec)) ".pdflog" )
                                           -- >> system makeIndexCommand
                                           -- disabled makeIndexCommand on non-windows, since it will always fail when absolute paths are used
                                           -- For some weird Latex reason this can only be avoided by setting an environment variable.
                                      where
                                      pdfLatexCommand = "pdflatex "++commonFlags++pdfgetOpts++ outputFile
                                      --makeIndexCommand = "makeglossaries "++replaceExtension outputFile "glo"
                                      --makeindex uses the error stream for verbose stuff...
                                      makeIndexCommand = "makeindex -s "++replaceExtension outputFile "ist"++" -t "++replaceExtension outputFile "glg"++" -o "++replaceExtension outputFile "gls"++" "++replaceExtension outputFile "glo 2> "++combine (dirOutput (getOpts fSpec)) "glossaries.log"
                                      pdfgetOpts = " -include-directory="++dirOutput (getOpts fSpec)++ " -output-directory="++dirOutput (getOpts fSpec)++" "
                                      commonFlags = "--halt-on-error --interaction=nonstopmode " -- MacTex options are normally with one '-', but '--interaction' is accepted
                                      -- we don't do --disable-installer on Windows, so the install dialog will pop up, even when we are in nonstopmode 
               _  -> return()

-----Linguistic goodies--------------------------------------

count :: Lang -> Int -> String -> String
count    lang    n      x
 = case (lang, n) of
      (Dutch  , 0) -> "geen "++plural Dutch x
      (Dutch  , 1) -> "één "++x
      (Dutch  , 2) -> "twee "++plural Dutch x
      (Dutch  , 3) -> "drie "++plural Dutch x
      (Dutch  , 4) -> "vier "++plural Dutch x
      (Dutch  , 5) -> "vijf "++plural Dutch x
      (Dutch  , 6) -> "zes "++plural Dutch x
      (Dutch  , _) -> show n++" "++plural Dutch x
      (English, 0) -> "no "++plural English x
      (English, 1) -> "one "++x
      (English, 2) -> "two "++plural English x
      (English, 3) -> "three "++plural English x
      (English, 4) -> "four "++plural English x
      (English, 5) -> "five "++plural English x
      (English, 6) -> "six "++plural English x
      (English, _) -> show n++" "++plural English x

------ Symbolic referencing ---------------------------------
data XRefObj = XRefNaturalLanguageDeclaration Declaration
             | XRefPredicateXpression Rule
             | XRefDataAnalRule Rule
             | XRefNaturalLanguageRule Rule
             | XRefProcessAnalysis Pattern
             | XRefProcessAnalysisDeclaration Declaration
             | XRefConceptualAnalysisPattern Pattern
             | XRefConceptualAnalysisDeclaration Declaration
             | XRefConceptualAnalysisRule Rule
             | XRefInterfacesInterface Interface
             | XRefNaturalLanguageTheme (Maybe Pattern) 
xRefTo :: XRefObj -> Inlines
xRefTo x = rawInline "latex"  $ xRefToLatexRefString x
xRefToLatexRefString :: XRefObj -> String
xRefToLatexRefString x = "\\ref{"++xRefRawLabel x++"}"
xRefRawLabel :: XRefObj -> String
xRefRawLabel x
 = case x of
     XRefNaturalLanguageDeclaration d -> "natLangDcl:"++(escapeNonAlphaNum.fullName) d
     XRefPredicateXpression r     -> "pex:"++(escapeNonAlphaNum.name) r
     XRefDataAnalRule r           -> "dataAnalRule:"++(escapeNonAlphaNum.name) r
     XRefNaturalLanguageRule r    -> "natLangRule:"++(escapeNonAlphaNum.name) r
     XRefProcessAnalysis p        -> "prcAnal:"++(escapeNonAlphaNum.name) p
     XRefProcessAnalysisDeclaration d 
                                  -> "prcAnalDcl:"++(escapeNonAlphaNum.fullName) d
     XRefConceptualAnalysisPattern p 
                                  -> "cptAnalPat:"++(escapeNonAlphaNum.name) p
     XRefConceptualAnalysisDeclaration d 
                                  -> "cptAnalDcl:"++(escapeNonAlphaNum.fullName) d
     XRefConceptualAnalysisRule r -> "cptAnalRule:"++(escapeNonAlphaNum.name) r
     XRefInterfacesInterface i    -> "interface:"++(escapeNonAlphaNum.name) i
     XRefNaturalLanguageTheme (Just t)
                                  -> "theme:"++(escapeNonAlphaNum.name) t
     XRefNaturalLanguageTheme Nothing 
                                  -> ":losseEindjes"
    where
      fullName d = name d++"*"++(name.source) d++"*"++(name.target) d                              

headerWithLabel :: XRefObj -> Int -> Inlines -> Blocks
headerWithLabel x = headerWith (xRefRawLabel x, [],[])

definitionListItemLabel :: XRefObj -> String -> Inlines
definitionListItemLabel x prefix
  = str prefix <> rawInline "latex" ("\\label{"++xRefRawLabel x++"}")


xrefCitation :: String -> Inline    -- uitbreidbaar voor andere rendering dan LaTeX
xrefCitation myLabel = RawInline (Text.Pandoc.Builder.Format "latex") ("\\cite{"++escapeNonAlphaNum myLabel++"}")

pandocEqnArray :: [[String]] -> [Block]
pandocEqnArray [] = []
pandocEqnArray xs
 = (toList . para . displayMath) 
       ( "\\begin{aligned}\n"
         ++ intercalate "\\\\\n   " [ intercalate "&" row  | row <-xs ]
         ++"\n\\end{aligned}"
       )
    
pandocEqnArrayWithLabels :: [(XRefObj,[String])] -> Blocks 
pandocEqnArrayWithLabels [] = mempty
pandocEqnArrayWithLabels rows
 = (para .displayMath) 
       ( "\\begin{aligned}\n"
         ++ intercalate "\\\\\n   " [ intercalate "&" row ++ "\\label{"++xRefRawLabel x++"}" | (x,row)<-rows ]
         ++"\n\\end{aligned}"
       )

pandocEqnArrayWithLabel :: XRefObj -> [[String]] -> Blocks
pandocEqnArrayWithLabel _ [] = mempty
pandocEqnArrayWithLabel xref rows
 = (para . displayMath)  
       ( "\\label{"++xRefRawLabel xref++"}\\begin{aligned}\\\\\n"
         ++ intercalate "\\\\\n   " [ intercalate "&" row | row <- rows ]
         ++"\n\\end{aligned}"
       )
                                
pandocEquation :: String -> [Block]
pandocEquation x = toList . para . displayMath $ x

pandocEquationWithLabel :: XRefObj -> String -> Blocks
pandocEquationWithLabel xref x = 
   para . displayMath $
        ( "\\begin{aligned}\\label{"++xRefRawLabel xref++"}\\\\\n"
           ++x
           ++"\n\\end{aligned}"
        )


--DESCR -> pandoc print functions for Ampersand data structures
---------------------------------------
-- LaTeX math markup
---------------------------------------

class ShowMath a where
 showMath :: a -> String

instance ShowMath A_Concept where
 showMath c = texOnly_Id (name c)

instance ShowMath A_Gen where
 showMath g@Isa{} = showMath (genspc g)++"\\ \\le\\ "++showMath (gengen g)
 showMath g@IsE{} = showMath (genspc g)++"\\ =\\ "++intercalate "\\cap" (map showMath (genrhs g))

instance ShowMath Rule where
 showMath r = showMath (rrexp r)

instance ShowMath Sign where
 showMath (Sign s t) = showMath s++"\\rel"++showMath t

instance ShowMath Expression where
 showMath = showExpr . insParentheses
   where  showExpr (EEqu (l,r)) = showExpr l++texOnly_equals++showExpr r
          showExpr (EImp (l,r)) = showExpr l++texOnly_subs++showExpr r
          showExpr (EIsc (l,r)) = showExpr l++texOnly_inter++showExpr r
          showExpr (EUni (l,r)) = showExpr l++texOnly_union++showExpr r
          showExpr (EDif (l,r)) = showExpr l++texOnly_bx ++showExpr r
          showExpr (ELrs (l,r)) = showExpr l++texOnly_lRes++showExpr r
          showExpr (ERrs (l,r)) = showExpr l++texOnly_rRes++showExpr r
          showExpr (EDia (l,r)) = showExpr l++texOnly_dia++showExpr r
          showExpr (ECps (EEps i sgn,r)) | i==source sgn||i==target sgn = showExpr  r
                                         | otherwise                    = showExpr (ECps (EDcI i,r))
          showExpr (ECps (l,EEps i sgn)) | i==source sgn||i==target sgn = showExpr  l
                                         | otherwise                    = showExpr (ECps (l,EDcI i))
          showExpr (ECps (l,r)) = showExpr l++texOnly_compose++showExpr r
          showExpr (ERad (l,r)) = showExpr l++texOnly_relAdd++showExpr r
          showExpr (EPrd (l,r)) = showExpr l++texOnly_crtPrd++showExpr r
          showExpr (EKl0 e)     = showExpr (addParensToSuper e)++"^{"++texOnly_star++"}"
          showExpr (EKl1 e)     = showExpr (addParensToSuper e)++"^{"++texOnly_plus++"}"
          showExpr (EFlp e)     = showExpr (addParensToSuper e)++"^{"++texOnly_flip++"}"
          showExpr (ECpl e)     = "\\cmpl{"++showExpr e++"}"
          showExpr (EBrk e)     = "("++showExpr e++")"
          showExpr (EDcD d)     = "\\id{"++name d++"}"
          showExpr (EDcI c)     = "I_{\\id{"++name c++"}}"
          showExpr  EEps{}      = "" -- fatal 417 "EEps may occur only in combination with composition (semicolon)."  -- SJ 2014-03-11: Are we sure about this? Let's see if it ever occurs...
          showExpr (EDcV sgn)   = "V_{\\id{"++show (source sgn)++"}\\times\\id{"++show (target sgn)++"}}"
          showExpr (EMp1 a _)   = "'{\\tt "++a++"}'"

-- add extra parentheses to consecutive superscripts, since latex cannot handle these
-- (this is not implemented in insParentheses because it is a latex-specific issue)
addParensToSuper :: Expression -> Expression
addParensToSuper e@EKl0{} = EBrk e
addParensToSuper e@EKl1{} = EBrk e
addParensToSuper e@EFlp{} = EBrk e
addParensToSuper e        = e

instance ShowMath Declaration where
 showMath decl@(Sgn{})
  = "\\declare{"++latexEscShw(name decl)++"}{"++latexEscShw(name (source decl))++"}{"++latexEscShw(name (target decl))++"}"
 showMath Isn{}
  = "\\mathbb{I}"
 showMath Vs{}
  = "\\full"

-- | latexEscShw escapes to LaTeX encoding. It is intended to be used in LaTeX text mode.
--   For more elaborate info on LaTeX encoding, consult the The Comprehensive LATEX Symbol List
--   on:    http://ftp.snt.utwente.nl/pub/software/tex/info/symbols/comprehensive/symbols-a4.pdf
latexEscShw :: String -> String
latexEscShw ""           = ""
latexEscShw ('\"':c:cs) | isAlphaNum c = "``"++latexEscShw (c:cs)
                        | otherwise    = "''"++latexEscShw (c:cs)
latexEscShw "\""        = "''"
latexEscShw (c:cs)      | isAlphaNum c && isAscii c = c:latexEscShw cs
                        | otherwise    = f c++latexEscShw cs
 where
  f '"' = "\\textquotedbl "
  f '#' = "\\#"
  f '$' = "\\$"
  f '%' = "\\%"
  f '&' = "\\&"
  f '\\'= "\\textbackslash "
  f '^' = "\\^{}"
  f '_' = "\\_"
  f '{' = "\\{"
  f '|' = "\\textbar "
  f '}' = "\\}"
  f '~' = "\\~{}"
  f '¦' = "\\textbrokenbar "
  f '¨' = "\\textasciidieresis "
  f '¯' = "\\textasciimacron "
  f '´' = "\\textasciiacute "
  f '¢' = "\\textcent "
  f '£' = "\\textpound "
  f '¤' = "\\textcurrency "
  f '¥' = "\\textyen "
  f '€' = "\\texteuro "
  f '<' = "\\textless "
  f '>' = "\\textgreater "
  f '±' = "\\textpm "
  f '«' = "\\guillemotleft "
  f '»' = "\\guillemotright "
  f '×' = "\\texttimes "
  f '÷' = "\\textdiv "
  f '§' = "\\S "
  f '©' = "\\textcopyright "
  f '¬' = "\\textlnot "
  f '®' = "\\textregistered "
  f '°' = "\\textdegree "
  f 'µ' = "\\textmu "
  f '¶' = "\\P "
  f '·' = "\\textperiodcentered "
  f '¼' = "\\textonequarter "
  f '½' = "\\textonehalf "
  f '¾' = "\\textthreequarters "
  f '¹' = "\\textonesuperior "
  f '²' = "\\texttwosuperior "
  f '³' = "\\textthreesuperior "
  f '∞' = "\\hbipropto "
  f 'ä' = "\\\"{a}"        --  umlaut or dieresis
  f 'Ä' = "\\\"{A}"        --  umlaut or dieresis
  f 'â' = "\\^{a}"         --  circumflex
  f 'Â' = "\\^{A}"         --  circumflex
  f 'à' = "\\`{a}"         --  grave accent
  f 'À' = "\\`{A}"         --  grave accent
  f 'á' = "\\'{a}"         --  acute accent
  f 'Á' = "\\'{A}"         --  acute accent
  f 'ã' = "\\~{a}"         --  tilde
  f 'Ã' = "\\~{A}"         --  tilde
  f 'å' = "\\aa "
--  f 'å' = "\\r{a}"       --  alternatively: ring over the letter
  f 'Å' = "\\AA "
--  f 'Å' = "\\r{A}"       --  alternatively: ring over the letter
  f 'ą' = "\\k{a}"         --  ogonek
  f 'Ą' = "\\k{A}"         --  ogonek
  f 'ª' = "\\textordfeminine "
  f 'æ' = "\\ae "
  f 'Æ' = "\\AE "
  f 'ç' = "\\c{c}"         --  cedilla
  f 'Ç' = "\\c{C}"         --  cedilla
  f 'Ð' = "\\DH "
  f 'ð' = "\\dh "
  f 'ë' = "\\\"{e}"        --  umlaut or dieresis
  f 'Ë' = "\\\"{E}"        --  umlaut or dieresis
  f 'ê' = "\\^{e}"         --  circumflex
  f 'Ê' = "\\^{E}"         --  circumflex
  f 'è' = "\\`{e}"         --  grave accent
  f 'È' = "\\`{E}"         --  grave accent
  f 'é' = "\\'{e}"         --  acute accent
  f 'É' = "\\'{E}"         --  acute accent
  f 'ï' = "\\\"{\\i}"      --  umlaut or dieresis
  f 'Ï' = "\\\"{I}"        --  umlaut or dieresis
  f 'î' = "\\^{\\i}"       --  circumflex
  f 'Î' = "\\^{I}"         --  circumflex
  f 'ì' = "\\`{\\i}"       --  grave accent
  f 'Ì' = "\\`{I}"         --  grave accent
  f 'í' = "\\'{\\i}"       --  acute accent
  f 'Í' = "\\'{I}"         --  acute accent
  f 'ł' = "\\l "           --  l with stroke
  f 'Ł' = "\\L "           --  l with stroke
  f 'n' = "\\~{n}"         --  tilde
  f 'Ñ' = "\\~{N}"         --  tilde
  f 'Ȯ' = "\\.{O}"         --  dot over the letter
  f 'ȯ' = "\\.{o}"         --  dot over the letter
  f 'ö' = "\\\"{o}"        --  umlaut or dieresis
  f 'Ö' = "\\\"{O}"        --  umlaut or dieresis
  f 'ô' = "\\^{o}"         --  circumflex
  f 'Ô' = "\\^{O}"         --  circumflex
  f 'ò' = "\\`{o}"         --  grave accent
  f 'Ò' = "\\`{O}"         --  grave accent
  f 'ó' = "\\'{o}"         --  acute accent
  f 'Ó' = "\\'{O}"         --  acute accent
  f 'õ' = "\\~{o}"         --  tilde
  f 'Õ' = "\\~{O}"         --  tilde
  f 'ō' = "\\={o}"         --  macron accent a bar over the letter)
  f 'Ō' = "\\={O}"         --  macron accent a bar over the letter)
  f 'ő' = "\\H{o}"         --  long Hungarian umlaut double acute)
  f 'Ő' = "\\H{O}"         --  long Hungarian umlaut double acute)
  f 'Ø' = "\\O "
  f 'ø' = "\\o "
  f 'º' = "\\textordmasculine "
  f 'ŏ' = "\\u{o}"         --  breve over the letter
  f 'Ŏ' = "\\u{O}"         --  breve over the letter
  f 'œ' = "\\oe "
  f 'Œ' = "\\OE "
  f 'š' = "\\v{s}"         --  caron/hacek "v") over the letter
  f 'Š' = "\\v{S}"         --  caron/hacek "v") over the letter
  f 'ß' = "\\ss "
  f 'Þ' = "\\TH "
  f 'þ' = "\\th "
  f '™' = "\\texttrademark "
  f 'ü' = "\\\"{u}"        --  umlaut or dieresis
  f 'Ü' = "\\\"{U}"        --  umlaut or dieresis
  f 'û' = "\\^{u}"         --  circumflex
  f 'Û' = "\\^{U}"         --  circumflex
  f 'ù' = "\\`{u}"         --  grave accent
  f 'Ù' = "\\`{U}"         --  grave accent
  f 'ú' = "\\'{u}"         --  acute accent
  f 'Ú' = "\\'{U}"         --  acute accent
  f 'ý' = "\\'{y}"         --  acute accent
  f 'Ý' = "\\'{Y}"         --  acute accent
  f _   = [c] -- let us think if this should be:    fatal 661 ("Symbol "++show x++" (character "++show (ord c)++") is not supported")

--posixFilePath :: FilePath -> String
-- tex uses posix file notation, however when on a windows machine, we have windows conventions for file paths...
-- To set the graphicspath, we want something like: \graphicspath{{"c:/data/Ampersand/output/"}}
--posixFilePath fp = "/"++System.FilePath.Posix.addTrailingPathSeparator (System.FilePath.Posix.joinPath   (tail  (splitDirectories fp)))


---------------------------
--- LaTeX related stuff ---
---------------------------

texOnly_Id :: String -> String
texOnly_Id s = "\\id{"++latexEscShw s++"} "

texOnly_fun :: String
texOnly_fun = "\\rightarrow "

texOnly_rel :: String
texOnly_rel = "\\times "

texOnly_compose :: String
texOnly_compose = ";"

texOnly_relAdd :: String
texOnly_relAdd = "\\dagger "

texOnly_crtPrd :: String
texOnly_crtPrd = "\\asterisk "

texOnly_inter :: String
texOnly_inter = "\\cap "

texOnly_union :: String
texOnly_union = "\\cup "

texOnly_subs :: String
texOnly_subs = "\\vdash "

texOnly_equals :: String
texOnly_equals = "="

texOnly_star :: String
texOnly_star = "^* "

texOnly_plus :: String
texOnly_plus = "^+ "

texOnly_bx :: String
texOnly_bx = " - "

texOnly_lRes :: String
texOnly_lRes = " / "

texOnly_rRes :: String
texOnly_rRes = " \\backslash "

texOnly_dia :: String
texOnly_dia = " \\Diamond "

texOnly_flip :: String
texOnly_flip = "\\smallsmile "

newGlossaryEntry :: String -> String -> Inlines
newGlossaryEntry nm cnt = 
  rawInline "latex"
    ("\\newglossaryentry{"++escapeNonAlphaNum nm ++"}\n"++
     "     { name={"++latexEscShw nm ++"}\n"++
     "     , description={"++latexEscShw (cnt)++"}}\n") 
