module Happstack.Authenticate.Password.Route where

import Control.Applicative   ((<$>))
import Control.Monad.Reader  (ReaderT, runReaderT)
import Control.Monad.Trans   (MonadIO(liftIO))
import Control.Concurrent.STM      (atomically)
import Control.Concurrent.STM.TVar (TVar, newTVar, readTVar)
import Data.Acid             (AcidState, closeAcidState, makeAcidic)
import Data.Acid.Local       (createCheckpointAndClose, openLocalStateFrom)
import Data.Text             (Text)
import Data.UserId           (UserId)
import Happstack.Authenticate.Core (AuthenticationHandler, AuthenticationMethod, AuthenticateConfig(..), AuthenticateState, AuthenticateURL, CoreError(..), toJSONError, toJSONResponse)
import Happstack.Authenticate.Password.Core (PasswordConfig(..), PasswordError(..), PasswordState, account, initialPasswordState, passwordReset, passwordRequestReset, token)
import Happstack.Authenticate.Password.Controllers (usernamePasswordCtrl)
import Happstack.Authenticate.Password.URL (PasswordURL(..), passwordAuthenticationMethod)
import Happstack.Authenticate.Password.Partials (routePartial)
import Happstack.Server      (Happstack, Response, ServerPartT, acceptLanguage, bestLanguage, lookTexts', mapServerPartT, ok, notFound, queryString, toResponse)
import Happstack.Server.JMacro ()
import HSP                   (unXMLGenT)
import HSP.HTML4             (html4StrictFrag)
import Language.Javascript.JMacro (JStat)
import System.FilePath       (combine)
import Text.Shakespeare.I18N (Lang)
import Web.Routes            (PathInfo(..), RouteT(..), mapRouteT, parseSegments)

------------------------------------------------------------------------------
-- routePassword
------------------------------------------------------------------------------

routePassword :: (Happstack m) =>
                 TVar PasswordConfig
              -> AcidState AuthenticateState
              -> TVar AuthenticateConfig
              -> AcidState PasswordState
              -> [Text]
              -> RouteT AuthenticateURL (ReaderT [Lang] m) Response
routePassword passwordConfigTV authenticateState authenticateConfigTV passwordState pathSegments =
  case parseSegments fromPathSegments pathSegments of
    (Left _) -> notFound $ toJSONError URLDecodeFailed
    (Right url) ->
      do authenticateConfig <- liftIO $ atomically $ readTVar authenticateConfigTV
         passwordConfig     <- liftIO $ atomically $ readTVar passwordConfigTV
         case url of
           Token        -> token authenticateState authenticateConfig passwordState
           Account mUrl -> toJSONResponse <$> account authenticateState passwordState authenticateConfig passwordConfig mUrl
           (Partial u)  -> do xml <- unXMLGenT (routePartial authenticateState u)
                              return $ toResponse (html4StrictFrag, xml)
           PasswordRequestReset -> toJSONResponse <$> passwordRequestReset authenticateConfig passwordConfig authenticateState passwordState
           PasswordReset        -> toJSONResponse <$> passwordReset authenticateState passwordState passwordConfig
           UsernamePasswordCtrl -> toResponse <$> usernamePasswordCtrl authenticateConfigTV

------------------------------------------------------------------------------
-- initPassword
------------------------------------------------------------------------------

initPassword :: PasswordConfig
             -> FilePath
             -> AcidState AuthenticateState
             -> TVar AuthenticateConfig
             -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)
initPassword passwordConfig basePath authenticateState authenticateConfigTV =
  do passwordState <- openLocalStateFrom (combine basePath "password") initialPasswordState
     passwordConfigTV <- atomically $ newTVar passwordConfig
     initPassword' passwordConfigTV passwordState basePath authenticateState authenticateConfigTV

initPassword' :: TVar PasswordConfig
              -> AcidState PasswordState
              -> FilePath
              -> AcidState AuthenticateState
              -> TVar AuthenticateConfig
              -> IO (Bool -> IO (), (AuthenticationMethod, AuthenticationHandler), RouteT AuthenticateURL (ServerPartT IO) JStat)
initPassword' passwordConfigTV passwordState basePath authenticateState authenticateConfigTV =
     do let shutdown = \normal ->
              if normal
              then createCheckpointAndClose passwordState
              else closeAcidState passwordState
            authenticationHandler pathSegments =
              do langsOveride <- queryString $ lookTexts' "_LANG"
                 langs        <- bestLanguage <$> acceptLanguage
                 mapRouteT (flip runReaderT (langsOveride ++ langs)) $
                   routePassword passwordConfigTV authenticateState authenticateConfigTV passwordState pathSegments
        pure (shutdown, (passwordAuthenticationMethod, authenticationHandler), usernamePasswordCtrl authenticateConfigTV)
