content: {-# LANGUAGE NoImplicitPrelude #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE OverloadedLists #-}
  
  module Cmt.IO.Config
      ( load
      , findFile
      , readCfg
      , checkFormat
      ) where
  
  import ClassyPrelude
  
  import Data.List        (nub)
  import System.Directory (doesFileExist, getCurrentDirectory, getHomeDirectory)
  import System.FilePath  (takeDirectory, (</>))
  
  import Cmt.Parser.Config (config)
  import Cmt.Types.Config
  import Cmt.Utility       ((<?>))
  
  configFile :: FilePath
  configFile = ".cmt"
  
  checkFormat :: Outputs -> Config -> Either Text (Config, Outputs)
  checkFormat output (Config parts format) = do
      let partNames = nub $ partName <$> parts
      let formatNames = nub . catMaybes $ formatName <$> format
      let result
              | isJust (find (== "*") formatNames) && null output = Left "Command line entry missing"
              | (length partNames + length output) /= length formatNames =
                  Left "Parts and format do not match"
              | otherwise = Right (Config parts format, output)
      result
  
  parse :: Outputs -> Text -> Either Text (Config, Outputs)
  parse output cfg = config cfg >>= checkFormat output
  
  read :: FilePath -> IO Text
  read path = decodeUtf8 <$> readFile path
  
  checkParent :: FilePath -> IO (Maybe FilePath)
  checkParent path = do
      let parent = takeDirectory path
      if parent /= path
          then checkDir parent
          else pure Nothing
  
  checkDir :: FilePath -> IO (Maybe FilePath)
  checkDir path = do
      let fp = path </> configFile
      exists <- doesFileExist fp
      if exists
          then pure $ Just fp
          else checkParent path
  
  xdgCfg :: IO (Maybe FilePath)
  xdgCfg = do
      fp <- (</> ".config" </> "cmt" </> configFile) <$> getHomeDirectory
      exists <- doesFileExist fp
      pure $ bool Nothing (Just fp) exists
  
  homeCfg :: IO (Maybe FilePath)
  homeCfg = do
      fp <- (</> configFile) <$> getHomeDirectory
      exists <- doesFileExist fp
      pure $ bool Nothing (Just fp) exists
  
  globalCfg :: IO (Maybe FilePath)
  globalCfg = do
      xdg <- xdgCfg
      home <- homeCfg
      pure $ xdg <?> home
  
  findFile :: IO (Maybe FilePath)
  findFile = do
      inDir <- checkDir =<< getCurrentDirectory
      case inDir of
          Just fp -> pure $ Just fp
          Nothing -> globalCfg
  
  load :: IO (Either Text Text)
  load = do
      exists <- findFile
      case exists of
          Just path -> Right <$> read path
          Nothing   -> pure $ Left ".cmt file not found"
  
  readCfg :: Outputs -> IO (Either Text (Config, Outputs))
  readCfg output = do
      cfg <- load
      pure (parse output =<< cfg)
  