module Cake.Rules where

import Cake.Core
import Cake.Process
import System.Directory
import System.FilePath
import Control.Applicative
import Control.Monad (when)
import Control.Monad.RWS (liftIO)
import qualified Parsek
import Parsek (completeResults, parse, Parser)
import Data.List
import Data.List.Split

------------------------------------------------------
-- Patterns
extension :: String -> P (String,String)
extension s = do
  base <- Parsek.many Parsek.anyChar
  Parsek.string s
  return (base++s,base)

anyExtension :: [String] -> P (String,String)
anyExtension ss = foldr (<|>) empty (map extension ss)

---------------------------------------------------------
-- Actions

copy :: FilePath -> FilePath -> Act()
copy from to = produce to $ needing [from] $ do
  mkdir $ takeDirectory to
  liftIO $ copyFile from to

mkdir :: FilePath -> Act ()    
mkdir d = liftIO $ createDirectoryIfMissing True d
    
touch :: FilePath -> Act ()
touch x = produce x $ do
  system ["touch",x]
  
readFile :: FilePath -> Act String
readFile x = do
  need x
  liftIO $ Prelude.readFile x
  
_pdflatex x = system ["pdflatex",x]
  
_bibtex x = system ["bibtex",x]

pandoc inp typ options = produce out $ do 
  need inp
  cut $ system $ ["pandoc",inp,"-t",typ,"-o",out] ++ options
 where out = replaceExtension inp typ
 
graphviz program inp typ options = produce out $ needing [inp] $ do
  system $ [program, "-T"++typ, "-o"++out, inp] ++ options
 where out = replaceExtension inp typ
  
{-
mpostDeriv = extension "-delayed.mp" ==> \s -> do
  let input = s ++ ".mp"
  need input
  rm (s ++ "-delayed.mp")
  mpost input
  mpost input
   -}

needing :: [FilePath] -> Act () -> Act ()
needing xs act = do
  mapM_ need xs
  cut act
          
--------------------------------------------------------------
-- Rules

simple outExt inExt f = extension outExt ==> \(output,base) ->
  let input = base ++ inExt 
  in  produce output $ needing [input]  $ f output input
  

tex_markdown_standalone = simple ".tex" ".markdown" $ \o i -> 
  pandoc i o ["--standalone"]
{-
html_markdown_standalone = simple ".html" ".markdown" $ \o i -> 
  system ["pandoc","--tab-stop=2","--standalone","-f","markdown","-t","latex", 
          "-o", o, i]
  -}
  
{-
tex_lhs = extension ".tex" $ \c -> do
  
  -- chase includes
  -}

  
pdf_tex = simple ".pdf" ".tex" $ \o i -> 
  system ["latexmk","-pdf",i]

getBibFiles input = distill (Custom ["bibfiles",input]) $ do
    
    ls <- map (drop 14) . filter ("\\bibliography{" `isPrefixOf`) . lines <$> Cake.Rules.readFile input
    let bibs = map (++".bib") $ case ls of
            [] -> []
            (l:_) -> splitOn "," $ reverse . dropWhile (== '}') . reverse $ l
    return $ Text bibs

includedTex input = do
    ls <- map (drop 7) . filter ("\\input{" `isPrefixOf`) . lines <$> Cake.Rules.readFile input
    let inputs = map (++".tex") $ case ls of
            [] -> []
            (l:_) -> splitOn "," $ reverse . dropWhile (== '}') . reverse $ l
    return $ inputs

chaseDeps i = do
  is <- includedTex i
  mapM_ chaseDeps is
  

pdflatexBibtex c = do
  let input = c ++ ".tex"
      aux = c ++ ".aux"
      pdf = c ++ ".pdf"
      bbl = c ++ ".bbl"
  produce pdf $ do 
        
    -- hack: if nothing changed in the aux file, there is no need to
    -- do the final pdflatex, because pdflatex has already produced the tex file.
    produce' aux $ do
      produce bbl $ do
        produce' aux $ do
          chaseDeps input
          cut $ _pdflatex c

        Text bibs <- getBibFiles input
        -- Note that this does not depend on the actual tex file; only the list of bibs. (aux1)
        mapM_ need bibs
        when (not $ null bibs) $
          cut $ _bibtex c
      cut $ _pdflatex c
    
    cut $ do _pdflatex c
             overwrote aux
             return ()

pdf_tex_bibtex = extension ".pdf" ==> \(_,c) -> pdflatexBibtex c

pdflatexBiblatex c = do
  let input = c ++ ".tex"
      aux = c ++ ".aux"
      pdf = c ++ ".pdf"
  produce pdf $ do
    produce aux $ do 
      chaseDeps input
      cut $ _pdflatex c
    
    produce (c ++ ".bbl") $ do
      -- Note that this does not depend on the actual tex file; only the list of bibs.
      Text bibs <- getBibFiles input
      mapM_ need bibs
      use aux
      when (not $ null bibs) $
        cut $ _bibtex c

    cut $ do _pdflatex c
             overwrote aux
             return ()
    

pdf_tex_biblatex = anyExtension [".pdf",".aux"] ==> \(_,c) -> 
  pdflatexBibtex c


_lhs2TeX i o = do
  system ["lhs2TeX","-o",o,i]

lhs2tex c = do
  let lhs = c ++ ".lhs"
      tex = c ++ ".tex"
  produce tex $ do
    need lhs
    _lhs2TeX lhs tex

tex_lhs = extension ".tex" ==> \(_,c) -> lhs2tex c

allRules = tex_markdown_standalone 
         <|> pdf_tex_biblatex
         <|> tex_lhs