module Heist.TestCommon where

------------------------------------------------------------------------------
import           Blaze.ByteString.Builder
import           Control.Monad.Trans
import           Control.Monad.Trans.Maybe  (MaybeT(..), runMaybeT)
import           Control.Monad.Trans.Except (ExceptT(ExceptT), runExceptT)
import           Data.ByteString.Char8      (ByteString)
import qualified Data.ByteString.Char8      as B
import           Data.Maybe
import           Data.Monoid


------------------------------------------------------------------------------
import           Heist
import qualified Heist.Compiled             as C
import           Heist.Internal.Types
import qualified Heist.Interpreted          as I
import qualified Heist.Interpreted.Internal as I
import qualified Text.XmlHtml               as X


------------------------------------------------------------------------------
-- | The default doctype given to templates
doctype :: ByteString
doctype = B.concat
    [ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
    , "'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>" ]


loadT :: MonadIO m
      => FilePath
      -> Splices (I.Splice m)
      -> Splices (I.Splice IO)
      -> Splices (C.Splice m)
      -> Splices (AttrSplice m)
      -> IO (Either [String] (HeistState m))
loadT baseDir a b c d = runExceptT $ do
    let sc = SpliceConfig (defaultInterpretedSplices `mappend` a)
                          (defaultLoadTimeSplices `mappend` b) c d
                          [loadTemplates baseDir] (const True)
    ExceptT $ initHeist $ HeistConfig sc "" False


------------------------------------------------------------------------------
loadIO :: FilePath
       -> Splices (I.Splice IO)
       -> Splices (I.Splice IO)
       -> Splices (C.Splice IO)
       -> Splices (AttrSplice IO)
       -> IO (Either [String] (HeistState IO))
loadIO baseDir a b c d = runExceptT $ do
    let sc = SpliceConfig (defaultInterpretedSplices >> a)
                          (defaultLoadTimeSplices >> b) c d
                          [loadTemplates baseDir] (const True)
    ExceptT $ initHeist $ HeistConfig sc "" False


------------------------------------------------------------------------------
loadHS :: FilePath -> IO (HeistState IO)
loadHS baseDir = do
    etm <- runExceptT $ do
        let sc = SpliceConfig defaultInterpretedSplices
                              defaultLoadTimeSplices mempty mempty
                              [loadTemplates baseDir] (const True)
        ExceptT $ initHeist $ HeistConfig sc "" False
    either (error . concat) return etm


loadEmpty :: Splices (I.Splice IO)
          -> Splices (I.Splice IO)
          -> Splices (C.Splice IO)
          -> Splices (AttrSplice IO)
          -> IO (HeistState IO)
loadEmpty a b c d = do
    let sc = SpliceConfig (defaultInterpretedSplices `mappend` a)
                          (defaultLoadTimeSplices `mappend` b) c d mempty
                          (const True)
    res <- initHeist $ HeistConfig sc "" False
    either (error . concat) return res


testTemplate :: FilePath -> ByteString -> IO ByteString
testTemplate tdir tname = do
    ts <- loadHS tdir
    Just (resDoc, _) <- I.renderTemplate ts tname
    return $ toByteString resDoc


testTemplateEval :: ByteString -> IO (Maybe Template)
testTemplateEval tname = do
    ts <- loadHS "templates"
    md <- evalHeistT (I.evalWithDoctypes tname) (X.TextNode "") ts
    return $ fmap X.docContent md


------------------------------------------------------------------------------
-- | Reloads the templates from disk and renders the specified
-- template.  (Old convenience code.)
quickRender :: FilePath -> ByteString -> IO (Maybe ByteString)
quickRender baseDir name = do
    ts  <- loadHS baseDir
    res <- I.renderTemplate ts name
    return (fmap (toByteString . fst) res)

cRender :: HeistState IO -> ByteString -> IO ByteString
cRender hs name = do
    builder <- fst $ fromJust $ C.renderTemplate hs name
    return $ toByteString builder

iRender :: HeistState IO -> ByteString -> IO ByteString
iRender hs name = do
    builder <- I.renderTemplate hs name
    return $ toByteString $ fst $ fromJust builder


------------------------------------------------------------------------------
isLeft :: Either e a -> Bool
isLeft (Left _) = True
isLeft _        = False


------------------------------------------------------------------------------
noteT :: Monad m => e -> MaybeT m a -> ExceptT e m a
noteT e ma = do
  x <- lift $ runMaybeT ma
  case x of
    Nothing -> ExceptT $ return (Left  e)
    Just a  -> ExceptT $ return (Right a)


------------------------------------------------------------------------------
hoistMaybe :: Monad m => Maybe a -> MaybeT m a
hoistMaybe = MaybeT . return
