    -- | Maintainer: Sean Whitton <spwhitton@spwhitton.name>

    module Propellor.Property.Locale where

    import Propellor.Base
    import Propellor.Property.File
    import qualified Propellor.Property.Apt as Apt

    import Data.List (isPrefixOf)

    type Locale = String
    type LocaleVariable = String

    -- | Select a locale for a list of global locale variables.
    --
    -- A locale variable is of the form @LC_BLAH@, @LANG@ or @LANGUAGE@.  See
    -- @locale(5)@.  One might say
    --
    --  >  & "en_GB.UTF-8" `Locale.selectedFor` ["LC_PAPER", "LC_MONETARY"]
    --
    -- to select the British English locale for paper size and currency conventions.
    --
    -- Note that reverting this property does not make a locale unavailable.  That's
    -- because it might be required for other Locale.selectedFor statements.
    selectedFor :: Locale -> [LocaleVariable] -> RevertableProperty DebianLike DebianLike
    locale `selectedFor` vars = select <!> deselect
      where
            select = tightenTargets $
                    check (not <$> isselected)
                            (cmdProperty "update-locale" selectArgs)
                            `requires` available locale
                            `describe` (locale ++ " locale selected")
            deselect = tightenTargets $
                    check isselected (cmdProperty "update-locale" vars)
                            `describe` (locale ++ " locale deselected")
            selectArgs = zipWith (++) vars (repeat ('=':locale))
            isselected = locale `isSelectedFor` vars

    isSelectedFor :: Locale -> [LocaleVariable] -> IO Bool
    locale `isSelectedFor` vars = do
            ls <- catchDefaultIO [] $ lines <$> readFile "/etc/default/locale"
            return $ and $ map (\v -> v ++ "=" ++ locale `elem` ls) vars

    -- | Ensures a locale is generated (or, if reverted, ensure it's not).
    --
    -- Fails if a locale is not available to be generated.  That is, a commented out
    -- entry for the locale and an accompanying charset must be present in
    -- /etc/locale.gen.
    --
    -- Per Debian bug #684134 we cannot ensure a locale is generated by means of
    -- Apt.reConfigure.  So localeAvailable edits /etc/locale.gen manually.
    available :: Locale -> RevertableProperty DebianLike DebianLike
    available locale = ensureAvailable `requires` Apt.installed ["locales"]
            <!> ensureUnavailable
      where
            f = "/etc/locale.gen"
            desc = (locale ++ " locale generated")
            ensureAvailable :: Property DebianLike
            ensureAvailable = property' desc $ \w -> do
                    locales <- lines <$> (liftIO $ readFile f)
                    if locale `presentIn` locales
                            then ensureProperty w $
                                    fileProperty desc (foldr uncomment []) f
                                            `onChange` regenerate
                            else error $ "locale " ++ locale ++ " is not present in /etc/locale.gen, even in commented out form; cannot generate"
            ensureUnavailable :: Property DebianLike
            ensureUnavailable = tightenTargets $
                    fileProperty (locale ++ " locale not generated") (foldr comment []) f
                    `onChange` regenerate

            uncomment l ls =
                    if ("# " ++ locale) `isPrefixOf` l
                    then drop 2 l : ls
                    else l:ls
            comment l ls =
                    if locale `isPrefixOf` l
                    then ("# " ++ l) : ls
                    else l:ls

            l `presentIn` ls = any (l `isPrefix`) ls
            l `isPrefix` x = (l `isPrefixOf` x) || (("# " ++ l) `isPrefixOf` x)

            regenerate = cmdProperty "locale-gen" []
                    `assume` MadeChange