{- This file is part of settings.
 -
 - Written in 2015 by fr33domlover <fr33domlover@rel4tion.org>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

module Data.Settings.Interface
    ( -- * By Route
      queryR
    , querySectionR
    , queryOptionR
    , updateOptionR
    , resetOptionR
      -- * By Path
    , query
    , querySection
    , queryOption
    , updateOption
    , resetOption
    )
where

import Control.Monad (liftM)
import Data.HashMap.Lazy (keys)
import Data.Settings.Route
import Data.Settings.Section
import Data.Settings.Types
import Prelude hiding (lookup)

-------------------------------------------------------------------------------
-- By Route
-------------------------------------------------------------------------------

-- | TODO
queryR :: MonadSettings m s
       => OptRoute
       -> m (Either SettingsError (Either ([SecName], [OptName]) String))
queryR route = do
    t <- getSTree
    case lookup route t of
        Just (Left sec)  ->
            return $ Right $ Left (keys $ secSubs sec, keys $ secOpts sec)
        Just (Right opt) -> liftM (Right . Right) $ optGet opt
        Nothing          -> return . Left $ NoSuchNode route

-- | TODO
querySectionR :: MonadSettings m s
              => OptRoute
              -> m (Either SettingsError ([SecName], [OptName]))
querySectionR route = do
    result <- queryR route
    return $ case result of
        Left err              -> Left err
        Right (Left secsOpts) -> Right secsOpts
        Right (Right _val)    -> Left $ NoSuchSection route

-- | TODO
queryOptionR :: MonadSettings m s
             => OptRoute
             -> m (Either SettingsError String)
queryOptionR route = do
    result <- queryR route
    return $ case result of
        Left err               -> Left err
        Right (Left _secsOpts) -> Left $ NoSuchOption route
        Right (Right val)      -> Right val

-- | TODO
updateOptionR :: MonadSettings m s
              => OptRoute
              -> String
              -> m (Maybe SettingsError)
updateOptionR route val = do
    t <- getSTree
    case lookupOpt route t of
        Just opt -> optSet opt val
        Nothing  -> return $ Just $ NoSuchOption route

-- | TODO
resetOptionR :: MonadSettings m s => OptRoute -> m (Maybe SettingsError)
resetOptionR route = do
    t <- getSTree
    case lookupOpt route t of
        Just opt -> optReset opt >> return Nothing
        Nothing  -> return $ Just $ NoSuchOption route

-------------------------------------------------------------------------------
-- By Path
-------------------------------------------------------------------------------

byPathMaybe :: Monad m
            => (OptRoute -> m (Maybe SettingsError))
            -> OptPath
            -> m (Maybe SettingsError)
byPathMaybe f path =
    case parseRoute path of
        Just route -> f route
        Nothing    -> return $ Just $ InvalidPath path

byPathEither :: Monad m
             => (OptRoute -> m (Either SettingsError a))
             -> OptPath
             -> m (Either SettingsError a)
byPathEither f path =
    case parseRoute path of
        Just route -> f route
        Nothing    -> return $ Left $ InvalidPath path

-- | TODO
query :: MonadSettings m s
      => OptPath
      -> m (Either SettingsError (Either ([SecName], [OptName]) String))
query = byPathEither queryR

-- | TODO
querySection :: MonadSettings m s
             => OptPath
             -> m (Either SettingsError ([SecName], [OptName]))
querySection = byPathEither querySectionR

-- | TODO
queryOption :: MonadSettings m s => OptPath -> m (Either SettingsError String)
queryOption = byPathEither queryOptionR

-- | TODO
updateOption :: MonadSettings m s
             => OptPath
             -> String
             -> m (Maybe SettingsError)
updateOption path val = byPathMaybe (flip updateOptionR val) path

-- | TODO
resetOption :: MonadSettings m s => OptPath -> m (Maybe SettingsError)
resetOption = byPathMaybe resetOptionR