{-# LANGUAGE DisambiguateRecordFields, TypeFamilies,
    StandaloneDeriving, DeriveFunctor, DeriveFoldable, GeneralizedNewtypeDeriving #-}

-------------------------------------------------------------------------------------
-- |
-- Copyright   : (c) Hans Hoglund 2012
-- License     : BSD-style
-- Maintainer  : hans@hanshoglund.se
-- Stability   : experimental
-- Portability : GHC
--
-- Renders module descriptions (and documentation) as plain Pandoc.
--
-------------------------------------------------------------------------------------

module Language.Modulo.Pandoc (
        -- ** Styles
        PandocStyle(..),
        stdPandocStyle,
        -- ** Rendering
        -- printModulePandoc,
        renderModulePandoc,
  ) where

import Data.Default
import Data.Semigroup
import Data.Char (chr)
import Data.Text (pack)

import Language.Modulo.C
import Language.Modulo.Util
import Language.Modulo.Util.Unmangle
import Language.Modulo
import qualified Language.Modulo.C as C
import qualified Language.Modulo.Haskell as Haskell
import qualified Language.Modulo.Lisp as Lisp

-- DEBUG        
import Control.Monad
import System.Directory
import System.Process
import Text.Pandoc.Options
import Text.Pandoc.Templates
import Language.Modulo.Load
import Language.Modulo.Parse
import Language.Modulo.Rename
-- DEBUG

import qualified Data.List as List
import Text.Pandoc.Definition
import Text.Pandoc.Writers.HTML
import Text.Pandoc.Writers.LaTeX
import Text.Pandoc.Readers.Markdown

data PandocStyle = PandocStyle
  deriving (Eq, Ord, Show)

stdMeta = Meta [] [][]
blockToPandoc = Pandoc stdMeta . return
blocksToPandoc = Pandoc stdMeta
mdStringToPandoc = readMarkdown def
instance Semigroup Pandoc where
  (<>) = mappend
instance Monoid Pandoc where
  mempty = Pandoc stdMeta mempty
  Pandoc m1 bs1 `mappend` Pandoc m2 bs2 = Pandoc (m1 {-First-}) (bs1 <> bs2)

stdPandocStyle = PandocStyle

renderModulePandoc :: Module -> Pandoc
renderModulePandoc = renderModulePandocStyle stdPandocStyle


renderModulePandocStyle :: PandocStyle -> Module -> Pandoc
renderModulePandocStyle st mod = Pandoc stdMeta [
  Header 2 ("", [], [("id",show $ modName mod)]) [Str (show $ modName mod)]
  -- CodeBlock nullAttr "import X.X.X",
  -- CodeBlock nullAttr "foo : Ptr -> Ptr"
  ] <> is <> ds
  where
    is = mconcat $fmap (uncurry $convertImport st) $ modImports mod
    ds = mconcat $fmap (uncurry $convertDocDecl st) $ modDecls mod

-- TODO link
convertImport :: PandocStyle -> ModuleName -> Maybe String -> Pandoc
convertImport st name conv = blockToPandoc $ CodeBlock nullAttr $ "import " ++ show name

convertDocDecl :: PandocStyle -> Doc -> Decl -> Pandoc
convertDocDecl st doc decl = blocksToPandoc [
  CodeBlock css $ unname $ getDeclName decl
  ]
  <>
  (mdStringToPandoc $ getDoc $doc)
  where
    -- unname = maybe "" (show . C.translFun def)
    -- unname = maybe "" getShortName
    unname = if isTypeDecl decl 
      then ("(defclass " ++) . (++ " ())") . maybe "" (Lisp.convertName def) 
      else (\x -> "(defun " ++ x ++ " (" ++ List.intercalate " " (argNames decl) ++ "))")    . maybe "" (Lisp.convertName def)
      where
        isTypeDecl (TypeDecl _ _) = True
        isTypeDecl _              = False

        argNames (FunctionDecl _ ft) = argNames2 ft
        argNames2 (Function as r) = fmap (showT.snd) as ++ [showT r]
        showT = show . Lisp.convertType def
        -- TODO #34 use name

    css = (".codeName", [], [("", "")])
    
    getShortName (QName _ n) = n



  
-- allFilesMatching :: FilePath -> (FilePath -> Bool) -> IO [FilePath]







main = documentFiles ["/Users/hans/audio/modules"] "/Users/hans/audio/modules"
-- main = documentFile ["/Users/hans/audio/modules"] "/Users/hans/audio/modules/Fa/Signal.module"

document :: [ModulePath] -> String -> IO Pandoc
document mpaths str = do
  mod <- unsafeRename mpaths . unsafeParse$ str
  putStr $ "Documenting " ++ show (modName mod) ++ "\n"
  return $ renderModulePandoc mod

documentFiles :: [ModulePath] -> FilePath -> IO ()
documentFiles mpaths path = do
  paths <- listFilesMatching path (List.isSuffixOf ".module")
  pandocs <- mapM (\path -> document mpaths =<< readFile path) paths
  strs <- toHtmlStr $ mconcat pandocs
  writeFile "test.html" $ strs
  return ()
    where
      cssBody = mempty
        <> "body > pre {                                      "
        <> "    padding:                20px;                 "
        <> "    margin-top:             15px;                 "
        <> "    margin-bottom:          15px;                 "
        <> "    background:             #faffff;              "
        <> "    border-radius:          12px;                 "
        <> "    border:                 1px solid LightGrey;  "
        <> "    box-shadow:             3px 3px 5px #eee;     "
        <> "    overflow:               auto;                 "
        <> "}"
        <> ""
        <> ""
        <> ""
        <> ""
        <> ""
        <> ""
        <> ""
        <> ""
      
      toHtmlStr :: Pandoc -> IO String
      toHtmlStr str = do
        Right templ <- getDefaultTemplate Nothing "html"
        return $ flip writeHtmlString str $ def {
          writerTemplate = templ,
          writerStandalone = True,
          writerTableOfContents = True,
          writerVariables = [("highlighting-css", cssBody)] }

documentFile :: [ModulePath] -> FilePath -> IO ()
documentFile mpaths path = do                 
  pd <- document mpaths =<< readFile path
  writeFile "test.html" $writeHtmlString def pd                 




unsafeRename :: [ModulePath] -> Module -> IO Module
unsafeRename paths m = do
    deps <- loadDependencies (withStdModulePaths paths) m
    return $ rename deps m
    
unsafeParse :: String -> Module
unsafeParse s = case (parse s) of
    Left e -> error $ "Parse error: " ++ show e
    Right m -> m
    

listFilesMatching :: FilePath -> (FilePath -> Bool) -> IO [FilePath]
listFilesMatching path pred = fmap (filter pred) $ listFilesR path

listFilesR :: FilePath -> IO [FilePath]
listFilesR = listFilesR' . (<> "/")

listFilesR' path = let
    isDODD :: String -> Bool
    isDODD f = not $ (List.isSuffixOf "/." f) || (List.isSuffixOf "/.." f)
    -- isDODD _ = True

    listDirs :: [FilePath] -> IO [FilePath]
    listDirs = filterM doesDirectoryExist . fmap (<> "/")

    listFiles :: [FilePath] -> IO [FilePath]
    listFiles = filterM doesFileExist

    joinFN :: String -> String -> FilePath
    joinFN p1 p2 = mconcat [p1, p2]

    in do
        allfiles <- getDirectoryContents path
        no_dots <- filterM (return . isDODD) (map (joinFN path) allfiles)
        dirs <- listDirs no_dots
        subdirfiles <- (mapM (listFilesR'{- . (<> "/")-}) dirs >>= return . concat)
        files <- listFiles no_dots
        return $ files ++ subdirfiles