-----------------------------------------------------------------------------
-- |
-- Module      :  Turbinado.Server.RequestProcess
-- Copyright   :  (c) Alson Kemp 2008, Niklas Broberg 2004,
-- License     :  BSD-style (see the file LICENSE)
-- 
-- Maintainer  :  Alson Kemp, Alson@AlsonKemp.com
-- Stability   :  experimental
-- Portability :  portable
--
-- A utility for the server that handles a recieved request, producing
-- a response.
--
-----------------------------------------------------------------------------
module Turbinado.Server.RequestProcess (
    processRequest
    ) where

import qualified Network.HTTP as HTTP
import Network.URI
import Prelude hiding (catch)
import System.Directory
import System.FilePath
import Control.Monad
import Data.Maybe
import Data.List
import Data.Dynamic

import Config.Master
import Turbinado.Controller.Routes
import Turbinado.Environment.Types
import Turbinado.Environment.CodeStore
import Turbinado.Environment.Logger
import Turbinado.Environment.Request
import qualified Turbinado.Environment.Routes as Routes
import Turbinado.Environment.Settings
import Turbinado.Server.Exception
import Turbinado.Controller
import Turbinado.View
import Turbinado.View.HTML
import Turbinado.Server.StandardResponse

-- | Filters to be run before the Controller are run
-- (e.g. a request logger or session initializer).  Filters
-- should consider what to do if isResponseComplete.  For example,
-- the Controller and View won't run if the response is complete
-- because they would risk overwriting the response.
--
-- NOTE: Custom filters are specified in Config/App.hs.
preFilters :: [Controller ()]
preFilters = [Routes.runRoutes ]

-- | Filters to be run after the Controller and View are run.
-- (e.g. cookie setter)
--
-- NOTE: Custom filters are specified in Config/App.hs.
postFilters :: [Controller ()]
postFilters = []

-- | The main request handler.  This runs standard and custom preFilters
-- then runs the Controller and View.
processRequest :: Controller ()
processRequest = do
          debugM $ " requestHandler : checking to see if the response is complete"
          complete <- isResponseComplete
          case complete of
                True -> do debugM $ " processRequest : response was already complete"
                           return ()
                False -> do debugM $ " requestHandler : running pre and main filters"
                            -- Run the Pre filters, the page
                            sequence_ $ preFilters ++
                                        customPreFilters ++
                                        [ retrieveAndRunController
                                        , checkFormats
                                        , retrieveAndRunLayout
                                        ] 
                            debugM $ " requestHandler : running post filters"
                            sequence_ (customPostFilters ++ postFilters)


-- | This function dynamically loads (if needed) the 'Controller'
-- using the information provided by the 'Routes'.  Controllers reside
-- in @App/Controllers@.
retrieveAndRunController :: Controller ()
retrieveAndRunController =
           do debugM $ " retrieveAndRunController : Starting"
              e <- getEnvironment
              complete <- isResponseComplete
              case complete of
                True -> do debugM $ " retrieveAndRunController : response was already complete"
                           return ()
                False -> do co <- getController
                            p  <- retrieveCode CTController co
                            case p of
                              CodeLoadController p' _ -> p'
                              CodeLoadFailure    e    -> errorResponse e
                              CodeLoadView       _  _ -> error "retrieveAndRunController: retrieveCode called, but returned CodeLoadView"
                              CodeLoadMissing         -> error "retrieveAndRunController: retrieveCode called, but returned CodeLoadMissing"

-- | This function dynamically loads (if needed) the 'View'
-- using the information provided by the 'Routes'.  Views reside
-- in @App/Views@ and Layouts reside in @App/Layouts@.  
-- The 'View' must contain a @markup@ function.
-- The first 'View' loaded is usually the Layout, which itself
-- loads the actual 'View'.  If the @layout@ setting is empty, then
-- no layout is loaded and the default 'View' is loaded.
retrieveAndRunLayout :: Controller ()
retrieveAndRunLayout =
           do e <- getEnvironment
              complete <- isResponseComplete
              case complete of
                True -> do debugM $ " retrieveAndRunLayout : response was already complete"
                           return ()
                False -> do l <- getSetting "layout"
                            p    <- case l of 
                                     Nothing -> do v <- getView
                                                   retrieveCode CTView v    -- If no Layout, then pull a View
                                     Just l' ->    retrieveCode CTLayout (l', "markup")
                            case p of
                              CodeLoadView       p' _ -> evalView p'
                              CodeLoadFailure    e    -> errorResponse e
                              CodeLoadController _  _ -> error "retrieveAndRunLayout: retrieveCode called, but returned CodeLoadController"
                              CodeLoadMissing         -> error "retrieveAndRunLayout: retrieveCode called, but returned CodeLoadMissing"


