content: {-# LANGUAGE ConstraintKinds        #-}
  {-# LANGUAGE FlexibleContexts       #-}
  {-# LANGUAGE FlexibleInstances      #-}
  {-# LANGUAGE FunctionalDependencies #-}
  {-# LANGUAGE MultiParamTypeClasses  #-}
  {-# LANGUAGE TemplateHaskell        #-}
  
  -- | This module contains enrinment for @importify cache@ command
  -- which is running inside @ReaderT env IO@ monad.
  
  module Importify.Environment
         ( -- * Base monad for @importify cache@ command
           RIO (..)
  
           -- * Environment for @importify cache@ command
         , CacheEnvironment (..)
         , HasGhcIncludeDir
         , HasPathToImportify
         , HasSaveSources
         , ghcIncludeDir
         , pathToImportify
         , pathToSymbols
         , saveSources
  
           -- * Runner for cache commend
         , runCache
         ) where
  
  import           Universum
  
  import           Lens.Micro.Platform (SimpleGetter, makeLensesWith, to)
  import           Path                (Abs, Dir, Path, (</>))
  import           Path.IO             (getCurrentDir)
  
  import           Extended.Lens.TH    (fieldsVerboseLensRules)
  import           Importify.Path      (doInsideDir, importifyPath, symbolsPath)
  import           Importify.Stack     (ghcIncludePath, stackProjectRoot)
  
  -- | 'ReaderT' + 'IO' monad described here:
  -- https://www.fpcomplete.com/blog/2017/07/the-rio-monad
  newtype RIO env a = RIO
      { runRIO :: ReaderT env IO a
      } deriving ( Functor, Applicative, Monad, MonadIO
                 , MonadReader env, MonadThrow, MonadCatch, MonadMask)
  
  -- | Environment for @importify cache@ command.
  data CacheEnvironment = CacheEnvironment
      { -- | Path to local @.importify@ folder
        _pathToImportify :: !(Path Abs Dir)
  
        -- | Path to GHC .h files
      , _ghcIncludeDir   :: !(Maybe (Path Abs Dir))
  
        -- | 'True' if unpacked sources should be stored locally as well
      , _saveSources     :: !Bool
      }
  
  makeLensesWith fieldsVerboseLensRules ''CacheEnvironment
  
  type HasPathToImportify env = HasPolyPathToImportify env (Path Abs Dir)
  type HasGhcIncludeDir   env = HasPolyGhcIncludeDir   env (Maybe (Path Abs Dir))
  type HasSaveSources     env = HasPolySaveSources     env Bool
  
  -- | Getter of @~\/path\/to\/project\/.importify\/symbols@ folder.
  pathToSymbols :: HasPathToImportify env => SimpleGetter env (Path Abs Dir)
  pathToSymbols = pathToImportify.to (</> symbolsPath)
  
  -- | Run @importify cache@ command. This function takes current
  -- project directory and searches for ghc include path.
  runCache :: Bool -> RIO CacheEnvironment () -> IO ()
  runCache _saveSources cacheAction = do
      projectRoot         <- runMaybeT stackProjectRoot
      case projectRoot of
          Nothing              -> return ()  -- error is reported inside 'stackProjectRoot'
          Just projectRootPath -> doInsideDir projectRootPath cacheRunner
    where
      cacheRunner :: IO ()
      cacheRunner = do
          projectPath         <- getCurrentDir
          let _pathToImportify = projectPath </> importifyPath
          _ghcIncludeDir      <- runMaybeT ghcIncludePath
          usingReaderT CacheEnvironment{..} $ runRIO cacheAction
  