module Text.Pandoc.R where

import           Control.Applicative
import           Control.Monad
import           Data.List           (delete)
import           Data.List.Split
import           Data.Maybe
import           System.Directory    (createDirectoryIfMissing, doesFileExist, removeFile, renameFile)
import           System.Exit         (ExitCode (..))
import           System.FilePath     ((<.>), (</>), pathSeparator)
import           System.IO           (hPutStrLn, stderr)
import           System.Process
import           Text.Pandoc.Generic
import           Text.Pandoc.JSON

rClass, defFile, defRplot, tmpRFile :: String
rClass = "Rplot"
defFile = "Rplot.png"
defRplot = "Rplots.pdf"
tmpRFile = "plot.R"

data Echo = Above | Below
   deriving (Read, Show)

renderRPandoc :: FilePath -> Bool -> Pandoc -> IO Pandoc
renderRPandoc f absolutePath p = bottomUpM (insertRplots f absolutePath) p

insertRplots :: FilePath -> Bool -> Block -> IO Block
insertRplots outDir absolutePath block@(CodeBlock (ident, classes, attrs) code) | rClass `elem` classes = do
   let imgFiles = readImgFiles attrs
   d <- renderRPlot code
   when (imgFiles == [defFile]) $ void $ convertDefault
   moveFiles imgFiles outDir
   let imgFiles' = map ((if absolutePath then pathSeparator : outDir else outDir) </>) imgFiles
   let imgBlock = Plain (map insertImage imgFiles')
   let codeBlock = CodeBlock (ident, "r":delete "Rplot" classes, attrs) code
   let block' = case readEcho attrs of
        (Just Above) -> Table [] [AlignLeft] [] [] [[[codeBlock]], [[imgBlock]]]
        (Just Below) -> Table [] [AlignLeft] [] [] [[[imgBlock]], [[codeBlock]]]
        Nothing -> imgBlock
   return $ if d then block' else block
insertRplots _ _ block = return block

readEcho :: [(String, String)] -> Maybe Echo
readEcho attrs = case lookup "echo" attrs of
   Just e  -> Just (read e)
   Nothing -> Nothing

readImgFiles :: [(String, String)] -> [FilePath]
readImgFiles attrs = case lookup "files" attrs of
   Just is -> splitOn "," is
   Nothing -> [defFile]

insertImage :: FilePath -> Inline
insertImage file = Image nullAttr [] (file,"")

--plot the R graph
--the files created will be the one specified in the R code with commands such as:
--png(filename="fig1.png")
--if no filename is specified, a file "Rplots.pdf" is generated.
renderRPlot :: String -> IO Bool
renderRPlot rcode = do
   writeFile tmpRFile rcode
   (code,stdout,stderr) <- readProcessWithExitCode "R" ["CMD", "BATCH", "--no-save", "--quiet", tmpRFile] ""
   when (code /= ExitSuccess) $ do
      putStrLnErr $ "R exited with: " ++ (show code)
      putStrLnErr stdout
      putStrLnErr stderr
   whenM (doesFileExist tmpRFile) $ removeFile tmpRFile
   whenM (doesFileExist "plot.Rout") $ removeFile "plot.Rout"
   return $ (code==ExitSuccess)

moveFiles :: [FilePath] -> FilePath -> IO ()
moveFiles files outDir = do
   createDirectoryIfMissing False outDir
   mapM_ (\a -> whenM (doesFileExist a) $ renameFile a (outDir </> a)) files


convertDefault :: IO Bool
convertDefault = do
   (code,_,_) <- readProcessWithExitCode "convert" [defRplot, defFile] ""
   whenM (doesFileExist defRplot) $ void $ removeFile defRplot
   return $ (code==ExitSuccess)

putStrLnErr = hPutStrLn stderr

whenM :: Monad m => m Bool -> m () -> m ()
whenM cond a = do
  c <- cond
  if c then a else (return ())