{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-
Copyright (C) 2009 John MacFarlane <jgm@berkeley.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-}

{- | Functions for initializing a Gitit wiki.
-}

module Network.Gitit.Initialize ( initializeGititState
                                , recompilePageTemplate
                                , compilePageTemplate
                                , createStaticIfMissing
                                , createRepoIfMissing
                                , createDefaultPages
                                , createTemplateIfMissing )
where
import System.FilePath ((</>), (<.>))
import Data.Semigroup ((<>))
import Data.Text (Text)
import qualified Data.Text as T
import Data.FileStore
import qualified Data.Map as M
import Network.Gitit.Util (readFileUTF8)
import Network.Gitit.Types
import Network.Gitit.State
import Network.Gitit.Framework
import Network.Gitit.Plugins
import Network.Gitit.Layout (defaultRenderPage)
import Paths_gitit (getDataFileName)
import Control.Exception (throwIO, try)
import System.Directory (copyFile, createDirectoryIfMissing, doesDirectoryExist, doesFileExist)
import Control.Monad ((<=<), unless, forM_, liftM)
import Text.Pandoc hiding (getDataFileName, WARNING)
import System.Log.Logger (logM, Priority(..))
import qualified Text.StringTemplate as ST


-- | Initialize Gitit State.
initializeGititState :: Config -> IO ()
initializeGititState conf = do
  let userFile' = userFile conf
      pluginModules' = pluginModules conf
  plugins' <- loadPlugins pluginModules'

  userFileExists <- doesFileExist userFile'
  users' <- if userFileExists
               then liftM (M.fromList . read . T.unpack) $ readFileUTF8 userFile'
               else return M.empty

  templ <- compilePageTemplate (templatesDir conf)

  updateGititState $ \s -> s { sessions      = Sessions M.empty
                             , users         = users'
                             , renderPage    = defaultRenderPage templ
                             , plugins       = plugins' }

-- | Recompile the page template.
recompilePageTemplate :: Config -> IO ()
recompilePageTemplate cfg = do
  ct <- compilePageTemplate (templatesDir cfg)
  updateGititState $ \st -> st{renderPage = defaultRenderPage ct}

--- | Compile a master page template named @page.st@ in the directory specified.
compilePageTemplate :: FilePath -> IO (ST.StringTemplate String)
compilePageTemplate tempsDir = do
  defaultGroup <- getDataFileName ("data" </> "templates") >>= ST.directoryGroup
  customExists <- doesDirectoryExist tempsDir
  combinedGroup <-
    if customExists
       -- default templates from data directory will be "shadowed"
       -- by templates from the user's template dir
       then do customGroup <- ST.directoryGroup tempsDir
               return $ ST.mergeSTGroups customGroup defaultGroup
       else do logM "gitit" WARNING $ "Custom template directory not found"
               return defaultGroup
  case ST.getStringTemplate "page" combinedGroup of
        Just t    -> return t
        Nothing   -> error "Could not get string template"

-- | Create templates dir if it doesn't exist.
createTemplateIfMissing :: Config -> IO ()
createTemplateIfMissing conf' = do
  templateExists <- doesDirectoryExist (templatesDir conf')
  unless templateExists $ do
    createDirectoryIfMissing True (templatesDir conf')
    templatePath <- getDataFileName $ "data" </> "templates"
    -- templs <- liftM (filter (`notElem` [".",".."])) $
    --  getDirectoryContents templatePath
    -- Copy footer.st, since this is the component users
    -- are most likely to want to customize:
    forM_ ["footer.st"] $ \t -> do
      copyFile (templatePath </> t) (templatesDir conf' </> t)
      logM "gitit" WARNING $ "Created " ++ (templatesDir conf' </> t)

-- | Create page repository unless it exists.
createRepoIfMissing :: Config -> IO ()
createRepoIfMissing conf = do
  let fs = filestoreFromConfig conf
  repoExists <- try (initialize fs) >>= \res ->
    case res of
         Right _               -> do
           logM "gitit" WARNING $ "Created repository in " ++ repositoryPath conf
           return False
         Left RepositoryExists -> return True
         Left e                -> throwIO e >> return False
  unless repoExists $ createDefaultPages conf

createDefaultPages :: Config -> IO ()
createDefaultPages conf = do
    let fs = filestoreFromConfig conf
        pt = defaultPageType conf
        toPandoc = readMarkdown def{ readerExtensions = enableExtension Ext_smart (readerExtensions def) }
        defOpts = def{ writerExtensions = if showLHSBirdTracks conf
                                             then enableExtension
                                                  Ext_literate_haskell
                                                  $ writerExtensions def
                                             else writerExtensions def
                     }
        -- note: we convert this (markdown) to the default page format
        converter = handleError . runPure . case pt of
                       Markdown   -> return
                       LaTeX      -> writeLaTeX defOpts <=< toPandoc
                       HTML       -> writeHtml5String defOpts <=< toPandoc
                       RST        -> writeRST defOpts <=< toPandoc
                       Textile    -> writeTextile defOpts <=< toPandoc
                       Org        -> writeOrg defOpts <=< toPandoc
#if MIN_VERSION_pandoc(3,0,0)
                       DocBook    -> writeDocBook5 defOpts <=< toPandoc
#else
                       DocBook    -> writeDocbook5 defOpts <=< toPandoc
#endif
                       MediaWiki  -> writeMediaWiki defOpts <=< toPandoc
                       CommonMark -> writeCommonMark defOpts <=< toPandoc

    welcomepath <- getDataFileName $ "data" </> "FrontPage" <.> "page"
    welcomecontents <- converter =<< readFileUTF8 welcomepath
    helppath <- getDataFileName $ "data" </> "Help" <.> "page"
    helpcontentsInitial <- converter =<< readFileUTF8 helppath
    markuppath <- getDataFileName $ "data" </> "markup" <.> show pt
    helpcontentsMarkup <- converter =<< readFileUTF8 markuppath
    let helpcontents = helpcontentsInitial <> "\n\n" <> helpcontentsMarkup
    usersguidepath <- getDataFileName "README.markdown"
    usersguidecontents <- converter =<< readFileUTF8 usersguidepath
    -- include header in case user changes default format:
    let header = "---\nformat: " <>
          T.pack (show pt) <> (if defaultLHS conf then "+lhs" else "") <>
          "\n...\n\n"
    -- add front page, help page, and user's guide
    let auth = Author "Gitit" ""
    createIfMissing fs (frontPage conf <.> defaultExtension conf) auth "Default front page"
      $ header <> welcomecontents
    createIfMissing fs ("Help" <.> defaultExtension conf) auth "Default help page"
      $ header <> helpcontents
    createIfMissing fs ("Gitit User’s Guide" <.> defaultExtension conf) auth "User’s guide (README)"
      $ header <> usersguidecontents

createIfMissing :: FileStore -> FilePath -> Author -> Description -> Text -> IO ()
createIfMissing fs p a comm cont = do
  res <- try $ create fs p a comm (T.unpack cont)
  case res of
       Right _ -> logM "gitit" WARNING ("Added " ++ p ++ " to repository")
       Left ResourceExists -> return ()
       Left e              -> throwIO e >> return ()

-- | Create static directory unless it exists.
createStaticIfMissing :: Config -> IO ()
createStaticIfMissing conf = do
  let staticdir = staticDir conf
  staticExists <- doesDirectoryExist staticdir
  unless staticExists $ do

    let cssdir = staticdir </> "css"
    createDirectoryIfMissing True cssdir
    cssDataDir <- getDataFileName $ "data" </> "static" </> "css"
    -- cssFiles <- liftM (filter (\f -> takeExtension f == ".css")) $ getDirectoryContents cssDataDir
    forM_ ["custom.css"] $ \f -> do
      copyFile (cssDataDir </> f) (cssdir </> f)
      logM "gitit" WARNING $ "Created " ++ (cssdir </> f)

    {-
    let icondir = staticdir </> "img" </> "icons"
    createDirectoryIfMissing True icondir
    iconDataDir <- getDataFileName $ "data" </> "static" </> "img" </> "icons"
    iconFiles <- liftM (filter (\f -> takeExtension f == ".png")) $ getDirectoryContents iconDataDir
    forM_ iconFiles $ \f -> do
      copyFile (iconDataDir </> f) (icondir </> f)
      logM "gitit" WARNING $ "Created " ++ (icondir </> f)
    -}

    logopath <- getDataFileName $ "data" </> "static" </> "img" </> "logo.png"
    createDirectoryIfMissing True $ staticdir </> "img"
    copyFile logopath $ staticdir </> "img" </> "logo.png"
    logM "gitit" WARNING $ "Created " ++ (staticdir </> "img" </> "logo.png")

    {-
    let jsdir = staticdir </> "js"
    createDirectoryIfMissing True jsdir
    jsDataDir <- getDataFileName $ "data" </> "static" </> "js"
    javascripts <- liftM (filter (`notElem` [".", ".."])) $ getDirectoryContents jsDataDir
    forM_ javascripts $ \f -> do
      copyFile (jsDataDir </> f) (jsdir </> f)
      logM "gitit" WARNING $ "Created " ++ (jsdir </> f)
    -}

