-- | This module is most near to be a drop-in replacement for @hgettext@ API.
-- It provides an @instance Localized IO@ by using process-level global variables (IORefs) for
-- storing current language and translations.
--
-- Being mostly an example, this module can though be usable for relatively simple applications,
-- for which you do not need to change languages a lot.
module Text.Localize.IO
  (setupTranslations,
   setLanguage, withLanguage,
   setContext, withContext
  ) where

import Data.IORef
import System.IO.Unsafe (unsafePerformIO)

import Text.Localize.Types
import Text.Localize.Load
import Text.Localize.Locale

currentContext :: IORef (Maybe Context)
currentContext = unsafePerformIO $ newIORef Nothing
{-# NOINLINE currentContext #-}

currentLanguage :: IORef LanguageId
currentLanguage = unsafePerformIO $ do
    language <- languageFromLocale
    newIORef language
{-# NOINLINE currentLanguage #-}

currentTranslations :: IORef Translations
currentTranslations = unsafePerformIO $ newIORef undefined
{-# NOINLINE currentTranslations #-}

instance Localized IO where
  getLanguage = readIORef currentLanguage
  getTranslations = readIORef currentTranslations
  getContext = readIORef currentContext

-- | This function must be called before any translation function call,
-- otherwise you will get runtime error.
--
-- Current language is selected from process locale at startup. You can
-- change it later by calling @setLanguage@ or @withLanguage@.
setupTranslations :: LocatePolicy -> IO ()
setupTranslations p = do
  translations <- locateTranslations p
  writeIORef currentTranslations translations

-- | Set current language.
setLanguage :: LanguageId -> IO ()
setLanguage language = writeIORef currentLanguage language

-- | Execute some actions with specific language, and
-- then return to previously used language.
withLanguage :: LanguageId -> IO a -> IO a
withLanguage language actions = do
  oldLang <- getLanguage
  setLanguage language
  result <- actions
  setLanguage oldLang
  return result

-- | Set current context.
setContext :: Maybe Context -> IO ()
setContext mbContext = writeIORef currentContext mbContext

-- | Execute some actions within specific context,
-- and then return to previously used context.
withContext :: Maybe Context -> IO a -> IO a
withContext ctxt actions = do
  oldContext <- getContext
  setContext ctxt
  result <- actions
  setContext oldContext
  return result