module Text.Press.Run where

import Control.Monad.State
import Control.Monad.Error (runErrorT, ErrorT)
import Control.Monad.Error.Class (throwError)
import Control.Monad.Writer.Lazy
import Prelude hiding (lookup)

import Data.Map (insert, lookup)
import Text.JSON (JSValue, decodeStrict, JSValue(..))

import Text.Press.Types
import Text.Press.Parser
import Text.Press.Render
import Text.Press.Tags

type Result = Either PressError [String]

runJSValuesWithPath datas templateName = runErrorT $ evalStateT (runJSValuesWithPathStTErrT datas templateName) defaultParser

runJSValuesWithPathStTErrT ::  [JSValue] -> String -> StateT Parser (ErrorT PressError IO) [String]
runJSValuesWithPathStTErrT datas templateName = do
    addToTemplateCache templateName
    parser <- get
    template <- fmap head $ lookupTemplates templateName
    let st = RenderState {
        renderStateParser = parser,
        renderStateTemplate = template,
        renderStateValues = datas
    }
    lift $ evalStateT (execWriterT doRender) st

runJSValuesWithBody :: [JSValue] -> String -> IO Result
runJSValuesWithBody jsvalues body = do 
    case parseString defaultParser body of
        Left parsecError -> return $ Left $ ParseError parsecError
        Right template -> runErrorT $ evalStateT (runJSValuesWithTemplateStTErrT jsvalues template) defaultParser

runJSValuesWithTemplate :: [JSValue] -> Template -> Parser -> IO Result
runJSValuesWithTemplate jsvalues template parser = runErrorT $ evalStateT (runJSValuesWithTemplateStTErrT jsvalues template) parser

runJSValuesWithTemplateStTErrT :: [JSValue] -> Template -> StateT Parser (ErrorT PressError IO) [String]
runJSValuesWithTemplateStTErrT jsvalues template = do
    case tmplExtends template of 
        Just s -> addToTemplateCache s
        Nothing -> return ()
    parser <- get
    lift $ evalStateT (execWriterT doRender) RenderState {
        renderStateParser = parser,
        renderStateTemplate = template,
        renderStateValues = jsvalues
    }

lookupTemplates templateName = do
    parser <- get
    case lookup templateName (parserTemplateCache parser) of
        Just template -> case tmplExtends template of 
            Nothing -> return [template]
            Just s -> do 
                xs <- lookupTemplates s
                return $ template : xs
        Nothing -> throwError $ PressError $ "unexpected uncached template: " ++ (show templateName)

addToTemplateCache template = do
    parser <- get
    let mapping = parserTemplateCache parser
    case lookup template mapping of 
        Just tmpl -> return ()
        Nothing -> do
            eitherTemplateError <- liftIO $ parseFile parser template
            case eitherTemplateError of
                Left err -> liftIO $ error . show $ err
                Right tmpl -> do 
                    let mapping' = insert template tmpl mapping 
                    put $ parser {parserTemplateCache = mapping'}
                    case tmplExtends tmpl of
                        Nothing -> return ()
                        Just s -> addToTemplateCache s

defaultParser = newParser { parserTagTypes = defaultTagTypes }