{-# LANGUAGE OverloadedStrings #-}
{-|
Module      : $header$
Copyright   : (c) Laurent P René de Cotret, 2019
License     : GNU GPL, version 2 or above
Maintainer  : laurent.decotret@outlook.com
Stability   : internal
Portability : portable

Configuration for pandoc-pyplot
-}

module Text.Pandoc.Filter.Pyplot.Configuration (
      configuration
    -- * For testing and internal purposes only
    , writeConfig
    , inclusionKeys
    , directoryKey
    , captionKey
    , dpiKey
    , includePathKey
    , saveFormatKey
    , withLinksKey
    , isTightBboxKey
    , isTransparentKey
) where

import           Data.Default.Class              (def)
import           Data.Maybe                      (fromMaybe)
import           Data.String                     (fromString)
import qualified Data.Text.IO                    as TIO
import           Data.Yaml
import           Data.Yaml.Config                (ignoreEnv, loadYamlSettings)

import           System.Directory                (doesFileExist)

import           Text.Pandoc.Filter.Pyplot.Types

-- | A @Configuration@ cannot be directly created from a YAML file
-- for two reasons:
--
--     * we want to store an include script. However, it makes more sense to
--       specify the script path in a YAML file.
--     * Save format is best specified by a string, and this must be parsed later
--
-- Therefore, we have another type, ConfigPrecursor, which CAN be created directly from
-- a YAML file.
data ConfigPrecursor
    = ConfigPrecursor
        { defaultDirectory_   :: FilePath
        , defaultIncludePath_ :: Maybe FilePath
        , defaultWithLinks_   :: Bool
        , defaultSaveFormat_  :: String
        , defaultDPI_         :: Int
        , tightBbox_          :: Bool
        , transparent_        :: Bool
        , interpreter_        :: String
        , flags_              :: [String]
        }

instance FromJSON ConfigPrecursor where
    parseJSON (Object v) =
        ConfigPrecursor
            <$> v .:? directoryKey     .!= (defaultDirectory def)
            <*> v .:? includePathKey
            <*> v .:? withLinksKey     .!= (defaultWithLinks def)
            <*> v .:? saveFormatKey    .!= (show $ defaultSaveFormat def)
            <*> v .:? dpiKey           .!= (defaultDPI def)
            <*> v .:? isTightBboxKey   .!= (isTightBbox def)
            <*> v .:? isTransparentKey .!= (isTransparent def)
            <*> v .:? "interpreter"    .!= (interpreter def)
            <*> v .:? "flags"          .!= (flags def)

    parseJSON _ = fail "Could not parse the configuration"


renderConfiguration :: ConfigPrecursor -> IO Configuration
renderConfiguration prec = do
    includeScript <- fromMaybe mempty $ TIO.readFile <$> defaultIncludePath_ prec
    let saveFormat' = fromString $ defaultSaveFormat_ prec
    return $ Configuration
        { defaultDirectory     = defaultDirectory_ prec
        , defaultIncludeScript = includeScript
        , defaultSaveFormat    = saveFormat'
        , defaultWithLinks     = defaultWithLinks_ prec
        , defaultDPI           = defaultDPI_ prec
        , isTightBbox          = tightBbox_ prec
        , isTransparent        = transparent_ prec
        , interpreter          = interpreter_ prec
        , flags                = flags_ prec
        }


-- | Building configuration from a YAML file. The
-- keys are exactly the same as for Markdown code blocks.
--
-- If a key is either not present or unreadable, its value will be set
-- to the default value.
--
-- @since 2.1.0.0
configuration :: FilePath -> IO Configuration
configuration fp = loadYamlSettings [fp] [] ignoreEnv >>= renderConfiguration


-- | Write a configuration to file. An exception will be raised in case the file would be overwritten.
--
-- @since 2.1.3.0
writeConfig :: FilePath -> Configuration -> IO ()
writeConfig fp config = do
    fileExists <- doesFileExist fp
    if fileExists
        then error $ mconcat ["File ", fp, " already exists."]
        else encodeFile fp config