{-#LANGUAGE NoImplicitPrelude #-}
{-#LANGUAGE OverloadedStrings #-}
{-#LANGUAGE LambdaCase #-}
{-#LANGUAGE ScopedTypeVariables #-}
{-#LANGUAGE FlexibleInstances #-}
{-#LANGUAGE MultiParamTypeClasses #-}
module Web.Sprinkles.Replacement
( Replacement
, expandReplacement
, expandReplacementText
)
where

import Web.Sprinkles.Prelude
import Data.Aeson as JSON
import Data.Aeson.TH as JSON
import Text.Ginger ( Template
                   , runGingerT
                   , parseGinger
                   , makeContextTextM
                   , ToGVal
                   , toGVal
                   , GVal (..)
                   , Run
                   )
import qualified Text.Ginger as Ginger
import Data.Default
import Control.Monad.Writer (Writer)
import Web.Sprinkles.Exceptions (formatException)
import Data.Text.Lazy.Builder as TextBuilder

data ReplacementItem =
    Literal Text |
    Variable Text
    deriving (Show, Eq)

newtype Replacement =
  Replacement (Template Ginger.SourcePos)
    deriving (Show)

instance FromJSON (Replacement) where
    parseJSON val = (either (fail . unpack . formatException) return . parseReplacement) =<< parseJSON val

expandReplacementText :: HashMap Text (GVal (Run Ginger.SourcePos IO Text))
                      -> Text
                      -> IO Text
expandReplacementText variables input =
    expandReplacement variables =<< either throwM return (parseReplacement input)

parseReplacement :: Text -> Either SomeException Replacement
parseReplacement input =
    either (Left . toException) (Right . Replacement) . runIdentity $
    parseGinger
        (const $ return Nothing)
        Nothing
        (unpack input)

expandReplacement :: HashMap Text (GVal (Run Ginger.SourcePos IO Text)) -> Replacement -> IO Text
expandReplacement variables (Replacement template) = do
    output <- newIORef (TextBuilder.fromText "")
    let emit :: Text -> IO ()
        emit t = modifyIORef' output (<> TextBuilder.fromText t)
        context = makeContextTextM lookupFn emit
        lookupFn varName = return . fromMaybe def $ lookup varName variables
    runGingerT context template
    toStrict . TextBuilder.toLazyText <$> readIORef output