module Snap.Snaplet.HeistNoClass
  ( Heist
  , DefaultMode(..)
  , heistInit
  , heistInit'
  , heistReloader
  , setInterpreted
  , getCurHeistConfig
  , clearHeistCache
  , addTemplates
  , addTemplatesAt
  , getHeistState
  , modifyHeistState
  , modifyHeistState'
  , withHeistState
  , withHeistState'
  , gRender
  , gRenderAs
  , gHeistServe
  , gHeistServeSingle
  , chooseMode
  , addConfig
  , cRender
  , cRenderAs
  , cHeistServe
  , cHeistServeSingle
  , render
  , renderAs
  , heistServe
  , heistServeSingle
  , heistLocal
  , withSplices
  , renderWithSplices
  , heistLocal'
  , withSplices'
  , renderWithSplices'
  , SnapletHeist
  , SnapletISplice
  , SnapletCSplice
  ) where
import           Prelude hiding ((.), id)
import           Control.Applicative
import           Control.Category
import           Control.Lens
import           Control.Monad.Reader
import           Control.Monad.State
import           Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
import           Data.DList (DList)
import qualified Data.HashMap.Strict as Map
import           Data.IORef
import           Data.Maybe
import qualified Data.Text as T
import           Data.Text.Encoding
import           System.FilePath.Posix
import           Heist
import qualified Heist.Compiled as C
import qualified Heist.Interpreted as I
import           Heist.Splices.Cache
#if !MIN_VERSION_base(4,8,0)
import           Data.Monoid
#endif
import           Snap.Snaplet
import           Snap.Snaplet.Heist.Internal
import           Snap.Core
import           Snap.Util.FileServe
changeState :: (HeistState (Handler a a) -> HeistState (Handler a a))
            -> Heist a
            -> Heist a
changeState _ (Configuring _)  =
    error "changeState: HeistState has not been initialized"
changeState f (Running hc hs cts dm) = Running hc (f hs) cts dm
clearHeistCache :: Heist b -> IO ()
clearHeistCache = clearCacheTagState . _heistCTS
                         
                         
                         
instance MonadSnap m => MonadSnap (HeistT n m) where
    liftSnap = lift . liftSnap
type SnapletHeist b m a = HeistT (Handler b b) m a
type SnapletCSplice b = SnapletHeist b IO (DList (Chunk (Handler b b)))
type SnapletISplice b = SnapletHeist b (Handler b b) Template
                          
                          
                          
heistInit :: FilePath
              
          -> SnapletInit b (Heist b)
heistInit = gHeistInit heistServe
heistInit' :: FilePath
               
           -> HeistConfig (Handler b b)
               
           -> SnapletInit b (Heist b)
heistInit' templateDir initialConfig =
    makeSnaplet "heist" "" Nothing $ heistInitWorker templateDir initialConfig
setInterpreted :: Snaplet (Heist b) -> Initializer b v ()
setInterpreted h =
    liftIO $ atomicModifyIORef (_heistConfig $ view snapletValue h)
        (\(hc,_) -> ((hc,Interpreted),()))
addTemplates :: Snaplet (Heist b)
             -> ByteString
                 
             -> Initializer b (Heist b) ()
addTemplates h urlPrefix = do
    snapletPath <- getSnapletFilePath
    addTemplatesAt h urlPrefix (snapletPath </> "templates")
addTemplatesAt :: Snaplet (Heist b)
               -> ByteString
                   
               -> FilePath
                   
               -> Initializer b (Heist b) ()
addTemplatesAt h urlPrefix templateDir = do
    rootUrl <- getSnapletRootURL
    let fullPrefix = (T.unpack $ decodeUtf8 rootUrl) </>
                     (T.unpack $ decodeUtf8 urlPrefix)
        addPrefix = addTemplatePathPrefix
                      (encodeUtf8 $ T.pack fullPrefix)
    ts <- liftIO $ (loadTemplates templateDir) >>=
                   either (error . concat) return
    printInfo $ T.pack $ unwords
        [ "...adding"
        , (show $ Map.size ts)
        , "templates from"
        , templateDir
        , "with route prefix"
        , fullPrefix ++ "/"
        ]
    let locations = [fmap addPrefix <$> loadTemplates templateDir]
        add (hc, dm) =
          ((over hcTemplateLocations (mappend locations) hc, dm), ())
    liftIO $ atomicModifyIORef (_heistConfig $ view snapletValue h) add
getCurHeistConfig :: Snaplet (Heist b)
                  -> Initializer b v (HeistConfig (Handler b b))
getCurHeistConfig h = case view snapletValue h of
    Configuring ref -> do
        (hc, _) <- liftIO $ readIORef ref
        return hc
    Running _ _ _ _ ->
        error "Can't get HeistConfig after heist is initialized."
getHeistState :: SnapletLens (Snaplet b) (Heist b)
              -> Handler b v (HeistState (Handler b b))
getHeistState heist = withTop' heist $ gets _heistState
modifyHeistState' :: SnapletLens (Snaplet b) (Heist b)
                  -> (HeistState (Handler b b) -> HeistState (Handler b b))
                  -> Initializer b v ()
modifyHeistState' heist f = do
    withTop' heist $ addPostInitHook $ return . Right . changeState f
modifyHeistState :: SnapletLens b (Heist b)
                 -> (HeistState (Handler b b) -> HeistState (Handler b b))
                 -> Initializer b v ()
modifyHeistState heist f = modifyHeistState' (subSnaplet heist) f
withHeistState' :: SnapletLens (Snaplet b) (Heist b)
                -> (HeistState (Handler b b) -> a)
                -> Handler b v a
withHeistState' heist f = do
    hs <- withTop' heist $ gets _heistState
    return $ f hs
withHeistState :: SnapletLens b (Heist b)
               -> (HeistState (Handler b b) -> a)
               -> Handler b v a
withHeistState heist f = withHeistState' (subSnaplet heist) f
addConfig :: Snaplet (Heist b)
          -> SpliceConfig (Handler b b)
          -> Initializer b v ()
addConfig h sc = case view snapletValue h of
    Configuring ref ->
        liftIO $ atomicModifyIORef ref add
    Running _ _ _ _ -> do
        printInfo "finalLoadHook called while running"
        error "this shouldn't happen"
  where
    add (hc, dm) =
      ((over hcSpliceConfig (`mappend` sc) hc, dm), ())
                            
                            
                            
iRenderHelper :: Maybe MIMEType
             -> ByteString
             -> Handler b (Heist b) ()
iRenderHelper c t = do
    (Running _ hs _ _) <- get
    withTop' id $ I.renderTemplate hs t >>= maybe pass serve
  where
    serve (b, mime) = do
        modifyResponse $ setContentType $ fromMaybe mime c
        writeBuilder b
cRenderHelper :: Maybe MIMEType
              -> ByteString
              -> Handler b (Heist b) ()
cRenderHelper c t = do
    (Running _ hs _ _) <- get
    withTop' id $ maybe pass serve $ C.renderTemplate hs t
  where
    serve (b, mime) = do
        modifyResponse $ setContentType $ fromMaybe mime c
        writeBuilder =<< b
serveURI :: Handler b (Heist b) ByteString
serveURI = do
    p <- getSafePath
    
    
    if take 1 p == "_" then pass else return $ B.pack p
render :: ByteString
           
       -> Handler b (Heist b) ()
render t = iRenderHelper Nothing t
renderAs :: ByteString
             
         -> ByteString
             
         -> Handler b (Heist b) ()
renderAs ct t = iRenderHelper (Just ct) t
heistServe :: Handler b (Heist b) ()
heistServe =
    ifTop (render "index") <|> (render =<< serveURI)
heistServeSingle :: ByteString -> Handler b (Heist b) ()
heistServeSingle t =
    render t <|> error ("Template " ++ show t ++ " not found.")
cRender :: ByteString
           
        -> Handler b (Heist b) ()
cRender t = cRenderHelper Nothing t
cRenderAs :: ByteString
             
          -> ByteString
             
          -> Handler b (Heist b) ()
cRenderAs ct t = cRenderHelper (Just ct) t
cHeistServe :: Handler b (Heist b) ()
cHeistServe =
    ifTop (cRender "index") <|> (cRender =<< serveURI)
cHeistServeSingle :: ByteString -> Handler b (Heist b) ()
cHeistServeSingle t =
    cRender t <|> error ("Template " ++ show t ++ " not found.")
chooseMode :: MonadState (Heist b1) m
           => m b
               
           -> m b
               
           -> m b
chooseMode cAction iAction = do
    mode <- gets _defMode
    case mode of
      Compiled -> cAction
      Interpreted -> iAction
gRender :: ByteString
           
        -> Handler b (Heist b) ()
gRender t = chooseMode (cRender t) (render t)
gRenderAs :: ByteString
             
          -> ByteString
             
          -> Handler b (Heist b) ()
gRenderAs ct t = chooseMode (cRenderAs ct t) (renderAs ct t)
gHeistServe :: Handler b (Heist b) ()
gHeistServe = chooseMode cHeistServe heistServe
gHeistServeSingle :: ByteString -> Handler b (Heist b) ()
gHeistServeSingle t = chooseMode (cHeistServeSingle t) (heistServeSingle t)
heistLocal' :: SnapletLens (Snaplet b) (Heist b)
            -> (HeistState (Handler b b) -> HeistState (Handler b b))
            -> Handler b v a
            -> Handler b v a
heistLocal' heist f m = do
    hs  <- withTop' heist get
    withTop' heist $ modify $ changeState f
    res <- m
    withTop' heist $ put hs
    return res
heistLocal :: SnapletLens b (Heist b)
           -> (HeistState (Handler b b) -> HeistState (Handler b b))
           -> Handler b v a
           -> Handler b v a
heistLocal heist f m = heistLocal' (subSnaplet heist) f m
withSplices' :: SnapletLens (Snaplet b) (Heist b)
             -> Splices (SnapletISplice b)
             -> Handler b v a
             -> Handler b v a
withSplices' heist splices m = do
    heistLocal' heist (I.bindSplices splices) m
withSplices :: SnapletLens b (Heist b)
            -> Splices (SnapletISplice b)
            -> Handler b v a
            -> Handler b v a
withSplices heist splices m = withSplices' (subSnaplet heist) splices m
renderWithSplices' :: SnapletLens (Snaplet b) (Heist b)
                   -> ByteString
                   -> Splices (SnapletISplice b)
                   -> Handler b v ()
renderWithSplices' heist t splices =
    withSplices' heist splices $ withTop' heist $ render t
renderWithSplices :: SnapletLens b (Heist b)
                  -> ByteString
                  -> Splices (SnapletISplice b)
                  -> Handler b v ()
renderWithSplices heist t splices =
    renderWithSplices' (subSnaplet heist) t splices