module Main where
 
+import Configuration (ServerConfiguration(..), defaultConfiguration, readConfiguration)
 import Control.Monad ((<=<))
-import Data.Maybe (fromMaybe, listToMaybe)
-import Network.Wai.Handler.Warp (Port(..), run)
+import Control.Monad.IO.Class (liftIO)
+import Data.Either (either)
+import Data.Maybe (maybe, listToMaybe)
+import Network.Wai.Handler.Warp (run)
 import Server
+import ServerMonad (ConfigMonad(..), ServerMonad(..))
 import System.Environment (getArgs)
 import Text.Read (readMaybe)
 
 main :: IO ()
-main = getPort >>= flip run app
+main = runConfigMonad getConfiguration >>= flip run app . configPort
 
-getPort :: IO Port
-getPort = getArgs >>= pure . fromMaybe 5000 . (readMaybe <=< listToMaybe)
+getConfiguration :: ConfigMonad ServerConfiguration
+getConfiguration = do
+  configFilePath <- liftIO getArgs >>= pure . (readMaybe <=< listToMaybe)
+  maybe (pure $ Right defaultConfiguration) readConfiguration configFilePath >>= pure . either error id
 
 - FlexibleContexts
 - FlexibleInstances
 - GADTs
+- GeneralizedNewtypeDeriving
 - KindSignatures
 - MultiParamTypeClasses
 - OverloadedStrings
 
--- /dev/null
+module Configuration where
+
+import Control.Monad ((<=<))
+import Data.Aeson (FromJSON(..), eitherDecodeStrict')
+import GHC.Generics (Generic(..))
+import Network.Wai.Handler.Warp (Port(..))
+import qualified Data.ByteString as B
+
+class (Monad m) => MonadReadConfig m where
+  readConfigFile :: FilePath -> m B.ByteString
+
+data ServerConfiguration = ServerConfiguration { configPort :: !Port
+                                               , configShowExceptions :: !Bool
+                                               } deriving (Generic)
+
+instance FromJSON ServerConfiguration
+
+defaultConfiguration :: ServerConfiguration
+defaultConfiguration = ServerConfiguration { configPort = 5000
+                                           , configShowExceptions = False
+                                           }
+
+readConfiguration :: (MonadReadConfig m) => FilePath -> m (Either String ServerConfiguration)
+readConfiguration = pure . eitherDecodeStrict' <=< readConfigFile
 
--- /dev/null
+module ServerMonad where
+
+import Configuration (ServerConfiguration(..), MonadReadConfig(..))
+import Control.Monad.IO.Class (MonadIO(..), liftIO)
+import Control.Monad.Reader (MonadReader(..), ReaderT(..))
+import qualified Data.ByteString as B
+
+newtype ConfigMonad a = ConfigMonad { runConfigMonad :: IO a
+                                    } deriving (Functor, Applicative, Monad, MonadIO)
+
+newtype ServerMonad a = ServerMonad { runServerMonad :: ReaderT ServerConfiguration IO a
+                                    } deriving (Functor, Applicative, Monad, MonadIO, MonadReader ServerConfiguration)
+
+instance MonadReadConfig ConfigMonad where
+  readConfigFile = liftIO . B.readFile