{-|
  
  Model definitions loader.

-}

module Snap.Snaplet.Redson.Snapless.Metamodel.Loader
    ( -- * Lower-level operations
      loadGroups
    , loadModel
    -- * High-level helper
    , loadModels
    )

where

import Control.Monad

import Data.Aeson as A

import Data.Functor

import qualified Data.ByteString.UTF8 as BU (fromString)
import qualified Data.ByteString.Lazy as LB (readFile)

import qualified Data.Map as M

import System.EasyFile


import Snap.Snaplet.Redson.Snapless.Metamodel


parseFile :: FromJSON a => FilePath -> IO (Maybe a)
parseFile filename = A.decode <$> LB.readFile filename


-- | Load groups from definitions file.
loadGroups :: FilePath -> IO (Maybe Groups)
loadGroups = parseFile


-- | Load model from specified location, performing group splicing,
-- applications and filling index cache.
loadModel :: FilePath
          -- ^ Path to model definition file
          -> Groups 
          -- ^ Group definitions
          -> IO (Maybe Model)
loadModel modelFile groups =
    do
      mres <- parseFile modelFile
      return $ case mres of
                 Just model -> Just $ 
                               cacheIndices $
                               doApplications $
                               spliceGroups groups model
                 Nothing -> Nothing


-- | Build metamodel name from its file path.
pathToModelName :: FilePath -> ModelName
pathToModelName filepath = BU.fromString $ takeBaseName filepath


-- | Read all models from directory to a map.
--
-- TODO: Perhaps rely on special directory file which explicitly lists
-- all models.
loadModels :: FilePath -- ^ Models directory
           -> FilePath -- ^ Group definitions file
           -> IO (M.Map ModelName Model)
loadModels directory groupsFile =
      do
        dirEntries <- getDirectoryContents directory
        -- Leave out non-files
        mdlFiles <- filterM doesFileExist
                 (map (\f -> directory ++ "/" ++ f) dirEntries)
        gs <- loadGroups groupsFile
        case gs of
          Just groups -> do
                  mdls <- mapM (\m -> do
                                  mres <- loadModel m groups
                                  return $ case mres of
                                    Just mdl -> mdl
                                    Nothing -> error $ "Could not parse " ++ m
                               ) mdlFiles
                  -- Splice groups & cache indices for served models
                  return $ M.fromList $
                         zip (map pathToModelName mdlFiles) mdls
          Nothing -> error $ "Bad groups file " ++ groupsFile