content: {-# Language CPP #-}
  -- | Settings are centralized, as much as possible, into this file. This
  -- includes database connection settings, static file locations, etc.
  -- In addition, you can configure a number of different aspects of Yesod
  -- by overriding methods in the Yesod typeclass. That instance is
  -- declared in the Foundation.hs file.
  module Settings where
  
  import ClassyPrelude.Yesod
  import Control.Exception           (throw)
  import Data.Aeson                  (Result (..), fromJSON, withObject, (.!=), (.:?))
  import Data.FileEmbed              (embedFile)
  import Data.Yaml                   (decodeEither')
  import Database.Persist.Postgresql (PostgresConf)
  import Language.Haskell.TH.Syntax  (Exp, Name, Q)
  import TH.RelativePaths            (pathRelativeToCabalPackage)
  import Network.Wai.Handler.Warp    (HostPreference)
  import Yesod.Default.Config2       (applyEnvValue)
  import Yesod.Default.Util          (WidgetFileSettings, widgetFileNoReload,
                                      widgetFileReload)
  
  -- | Runtime settings to configure this application. These settings can be
  -- loaded from various sources: defaults, environment variables, config files,
  -- theoretically even a database.
  data AppSettings = AppSettings
      { appStaticDir              :: String
      -- ^ Directory from which to serve static files.
      , appDatabaseConf           :: PostgresConf
      -- ^ Configuration settings for accessing the database.
      , appRoot                   :: Text
      -- ^ Base for generated URLs. Includes protocol!
      , appStaticRoot             :: Maybe Text
      -- ^ Base for static URLs. Includes protocol!
      , appHost                   :: HostPreference
      -- ^ Host/interface the server should bind to.
      , appPort                   :: Int
      -- ^ Port to listen on
      , appIpFromHeader           :: Bool
      -- ^ Get the IP address from the header when logging. Useful when sitting
      -- behind a reverse proxy.
  
      , appSqlite                 :: Bool
      -- ^ Use Sqlite rather than postgres
      , appDataRoot               :: FilePath
      -- ^ root for stored application data
      , appBookRoot               :: FilePath
      -- ^ root for the carnap book
      , appDetailedRequestLogging :: Bool
      -- ^ Use detailed request logging system
      , appShouldLogAll           :: Bool
      -- ^ Should all log messages be displayed?
      , appReloadTemplates        :: Bool
      -- ^ Use the reload version of templates
      , appMutableStatic          :: Bool
      -- ^ Assume that files in the static dir may change after compilation
      , appSkipCombining          :: Bool
      -- ^ Perform no stylesheet/script combining
  
      -- Example app-specific configuration values.
      , appCopyright              :: Text
      -- ^ Copyright text to appear in the footer of the page
      , appAnalytics              :: Maybe Text
      -- ^ Google Analytics code
      , appDevel :: Bool
      -- Flag to indicate development mode
      , appKey :: Text
      -- API Key from Google
      , appSecret:: Text
      -- API Secret from Google
  
      }
  
  instance FromJSON AppSettings where
      parseJSON = withObject "AppSettings" $ \o -> do
  #if DEVELOPMENT
          let appDevel = True
  #else
          let appDevel = False
  #endif
          appStaticDir              <- o .: "static-dir"
          appDatabaseConf           <- o .: "database"
          appRoot                   <- o .: "approot"
          appHost                   <- fromString <$> o .: "host"
          appPort                   <- o .: "port"
          appIpFromHeader           <- o .: "ip-from-header"
  
          appSqlite                 <- o .:  "sqlite"           .!= False
          appDataRoot               <- o .:? "data-root"        .!= "."
          appStaticRoot             <- o .:? "static-root"
          appBookRoot               <- o .:? "book-root"        .!= "."
          appDetailedRequestLogging <- o .:? "detailed-logging" .!= appDevel
          appShouldLogAll           <- o .:? "should-log-all"   .!= appDevel
          appReloadTemplates        <- o .:? "reload-templates" .!= appDevel
          appMutableStatic          <- o .:? "mutable-static"   .!= appDevel
          appSkipCombining          <- o .:? "skip-combining"   .!= appDevel
  
          appCopyright              <- o .: "copyright"
          appAnalytics              <- o .:? "analytics"
          appKey                    <- o .:? "google-api-key"   .!= ""
          appSecret                 <- o .:? "google-secret"    .!= ""
  
          return AppSettings {..}
  
  -- | Settings for 'widgetFile', such as which template languages to support and
  -- default Hamlet settings.
  --
  -- For more information on modifying behavior, see:
  --
  -- https://github.com/yesodweb/yesod/wiki/Overriding-widgetFile
  widgetFileSettings :: WidgetFileSettings
  widgetFileSettings = def
  
  -- | How static files should be combined.
  combineSettings :: CombineSettings
  combineSettings = def
  
  -- The rest of this file contains settings which rarely need changing by a
  -- user.
  
  widgetFile :: String -> Q Exp
  widgetFile = (if appReloadTemplates compileTimeAppSettings
                  then widgetFileReload
                  else widgetFileNoReload)
                widgetFileSettings
  
  -- | Raw bytes at compile time of @config/settings.yml@
  configSettingsYmlBS :: ByteString
  configSettingsYmlBS = $(embedFile =<< pathRelativeToCabalPackage "config/settings.yml")
  
  -- | @config/settings.yml@, parsed to a @Value@.
  configSettingsYmlValue :: Value
  configSettingsYmlValue = either throw id $ decodeEither' configSettingsYmlBS
  
  -- | A version of @AppSettings@ parsed at compile time from @config/settings.yml@.
  compileTimeAppSettings :: AppSettings
  compileTimeAppSettings =
      case fromJSON $ applyEnvValue False mempty configSettingsYmlValue of
          Error e -> error e
          Success settings -> settings
  
  -- The following two functions can be used to combine multiple CSS or JS files
  -- at compile time to decrease the number of http requests.
  -- Sample usage (inside a Widget):
  --
  -- > $(combineStylesheets 'StaticR [style1_css, style2_css])
  
  combineStylesheets :: Name -> [Route Static] -> Q Exp
  combineStylesheets = combineStylesheets'
      (appSkipCombining compileTimeAppSettings)
      combineSettings
  
  combineScripts :: Name -> [Route Static] -> Q Exp
  combineScripts = combineScripts'
      (appSkipCombining compileTimeAppSettings)
      combineSettings
  