{-|
Pencil Config.
-}
module Pencil.Config
  ( Config(..)
  , defaultConfig
  , getSourceDir , setSourceDir
  , getOutputDir , setOutputDir
  , getEnv , setEnv , updateEnv
  , getDisplayValue , setDisplayValue
  , getSassOptions , setSassOptions
  , getPandocReaderOptions , setPandocReaderOptions
  , getPandocWriterOptions , setPandocWriterOptions
  )where

import Data.Default (Default)
import Pencil.Env.Internal
import Text.Pandoc.Extensions (disableExtension, Extension(..))
import Text.Sass.Options (defaultSassOptions)
import qualified Data.HashMap.Strict as H
import qualified Data.Text as T
import qualified Text.Pandoc as P
import qualified Text.Pandoc.Highlighting
import qualified Text.Sass as Sass

-- | The main @Config@ needed to build your website. Your app's @Config@ is
-- passed into the 'Pencil.App.PencilApp' monad transformer.
--
-- Use 'defaultConfig' as a starting point, along with the config-modification
-- helpers such as 'setSourceDir'.
--
data Config = Config
  { configSourceDir :: FilePath
  , configOutputDir :: FilePath
  , configEnv :: Env
  , configDisplayValue :: Value -> T.Text
  , configSassOptions :: Sass.SassOptions
  , configPandocReaderOptions :: P.ReaderOptions
  , configPandocWriterOptions :: P.WriterOptions
  }

-- 'Data.Default.Default' instance for 'Config'.
instance Default Config where
  def = defaultConfig

-- | This default @Config@ gives you everything you need to start.
--
-- Default values:
--
-- @
-- Config
--  { 'configSourceDir' = "site/"
--  , 'configOutputDir' = "out/"
--  , 'configEnv' = HashMap.empty
--  , 'configDisplayValue' = 'toText'
--  , 'configSassOptions' = Text.Sass.Options.defaultSassOptions
--  , 'configPandocReaderOptions' = Text.Pandoc.def {
--       Text.Pandoc.readerExtensions = 'Text.Pandoc.Extensions.disableExtension' 'Text.Pandoc.Extensions.Ext_tex_math_dollars' ('Text.Pandoc.Extensions.getDefaultExtensions' "markdown")
--    }
--  , 'configPandocWriterOptions' = Text.Pandoc.def { Text.Pandoc.writerHighlightStyle = Just Text.Pandoc.Highlighting.monochrome }
--  , 'configDisplayValue = 'toText'
--  }
-- @
--
-- @Ext_tex_math_dollars@ is disabled because it messes with parsing template
-- variable directives. If you want TeX math, the better option is drop in a
-- JavaScript library like KaTeX (https://katex.org).
--
defaultConfig :: Config
defaultConfig = Config
  { configSourceDir = "site/"
  , configOutputDir = "out/"
  , configEnv = H.empty
  , configSassOptions = Text.Sass.Options.defaultSassOptions

  -- For markdown reader. We use getDefaultExtensions "markdown" here to get default Markdown extensions.
  -- See
  -- https://hackage.haskell.org/package/pandoc/docs/Text-Pandoc-Extensions.html#v:getDefaultExtensions
  --
  -- Ext_text_math_dollars is disabled because it messes with variable
  -- directives. For example, this renders weird (as of Pandoc 2.7.2):
  -- > **${name}** and **${age}**
  , configPandocReaderOptions = P.def {
      P.readerExtensions = disableExtension Ext_tex_math_dollars (P.getDefaultExtensions "markdown")
  }
  , configPandocWriterOptions = P.def {
      P.writerHighlightStyle = Just Text.Pandoc.Highlighting.monochrome
  }
  , configDisplayValue = toText
  }

-- | Gets the source directory of your web page source files.
getSourceDir :: Config -> FilePath
getSourceDir = configSourceDir

-- | Sets the source directory of your web page source files.
setSourceDir :: FilePath -> Config -> Config
setSourceDir fp c = c { configSourceDir = fp }

-- | Gets the output directory of your rendered web pages.
getOutputDir :: Config -> FilePath
getOutputDir = configOutputDir

-- | Sets the output directory of your rendered web pages.
setOutputDir :: FilePath -> Config -> Config
setOutputDir fp c = c { configOutputDir = fp }

-- | Gets environment of the @Config@, which is what the @PencilApp@ monad
-- transformer uses. This is where variables are set for rendering template
-- directives.
getEnv :: Config -> Env
getEnv = configEnv

-- | Sets the current environment. You may also want to look at 'Pencil.App.withEnv' if you
-- want to 'Pencil.Content.render' things in a modified environment.
setEnv :: Env -> Config -> Config
setEnv env c = c { configEnv = env }

-- | Updates the Env inside the 'Config'.
updateEnv :: (Env -> Env) -> Config -> Config
updateEnv f c = c { configEnv = f (getEnv c) }

-- | Gets the 'Sass.SassOptions' for rendering Sass/Scss files.
getSassOptions :: Config -> Sass.SassOptions
getSassOptions = configSassOptions

-- | Sets the 'Sass.SassOptions' for rendering Sass/Scss files.
setSassOptions :: Sass.SassOptions -> Config -> Config
setSassOptions env c = c { configSassOptions = env }

-- | Gets the 'Text.Pandoc.ReaderOptions' for reading files that use Pandoc.
-- Supported formats:
--
-- * Markdown
-- * Open a GitHub issue if you'd like to see more options!
--
getPandocReaderOptions :: Config -> P.ReaderOptions
getPandocReaderOptions = configPandocReaderOptions

-- | Sets the 'Text.Pandoc.ReaderOptions'. For example, you may want to enable
-- some Pandoc extensions like 'Text.Pandoc.Extensions.Ext_literate_haskell':
--
-- @
-- setPandocReaderOptions
--   (Text.Pandoc.def { 'Text.Pandoc.Options.readerExtensions' = extensionsFromList [Ext_literate_haskell] })
--   config
-- @
setPandocReaderOptions :: P.ReaderOptions -> Config -> Config
setPandocReaderOptions o c = c { configPandocReaderOptions = o }

-- | Gets the 'Text.Pandoc.WriterOptions' for rendering files that use Pandoc.
getPandocWriterOptions :: Config -> P.WriterOptions
getPandocWriterOptions = configPandocWriterOptions

-- | Sets the 'Text.Pandoc.WriterOptions'.
setPandocWriterOptions :: P.WriterOptions -> Config -> Config
setPandocWriterOptions o c = c { configPandocWriterOptions = o }

-- | Gets the function that renders 'Value' to text.
getDisplayValue :: Config -> Value -> T.Text
getDisplayValue = configDisplayValue

-- | Sets the function that renders 'Value' to text. Overwrite this with your
-- own function if you would like to change how certain 'Value's are rendered
-- (e.g. 'Pencil.Env.Internal.VDateTime').
--
-- @
-- myRender :: Value -> T.Text
-- myRender (VDateTime dt) = 'T.pack' $ 'TF.formatTime' 'TF.defaultTimeLocale' "%e %B %Y" dt
-- myRender t = 'toText' t
--
-- ...
--
-- setDisplayValue myRender config
-- @
--
-- In the above example, we change the @VDateTime@ rendering to show @25
-- December 2017@. Leave everything else unchanged.
--
setDisplayValue :: (Value -> T.Text) -> Config -> Config
setDisplayValue f c = c { configDisplayValue = f }