{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, ExistentialQuantification, DeriveDataTypeable #-}
-- | This is the main module of the @localize@ package.
-- It contains definitions of general use and reexport generally required internal modules.
module Text.Localize
  (
   -- $description
   --
   -- * Most used functions
   __, __n, __f,
   -- * Basic functions
   translate, translateN, translateNFormat, 
   lookup, withTranslation,
   -- * Reexports
   module Text.Localize.Types,
   module Text.Localize.Load,
   module Text.Localize.Locale
  ) where

import Prelude hiding (lookup)
import Control.Applicative
import Control.Monad
import qualified Data.Map as M
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as TLE
import qualified Data.Text.Encoding as TE
import Data.String
import Data.List hiding (lookup)
import Data.Monoid
import Data.Typeable
import qualified Data.Gettext as Gettext
import qualified Data.Text.Format.Heavy as F
import Data.Text.Format.Heavy.Parse (parseFormat)

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

-- $description
--
-- This is the main module of the @localize@ package. In most cases, you have to import only
-- this module. In specific cases, you may also want to import Text.Localize.IO or Text.Localize.State.
--
-- All functions exported by @localize@ package work with any instance of the
-- @Localized@ type class. There are two simple examples of this type class
-- implementation provided in separate modules (IO and State); however, in
-- complex applications it may be more convinient to implement @Localized@
-- instance for the monadic stack you already have.
--
-- Example of usage is:
--
-- @
-- import qualified Data.Text as T
-- import qualified Data.Text.Lazy.IO as TLIO
-- import Text.Localize
-- 
-- newtype MyMonad a = MyMonad {unMyMonad :: ... }
--   deriving (Monad)
-- 
-- instance Localized MyMonad where
--   ...
--
-- runMyMonad :: Translations -> MyMonad a -> IO a
-- runMyMonad = ...
-- 
-- hello :: T.Text -> MyMonad ()
-- hello name = do
--   liftIO $ TLIO.putStrLn =<< __ "Your name: "
--   liftIO $ hFlush stdout
--   name <- liftIO $ TLIO.getLine
--   greeting <- __f "Hello, {}!" (Single name)
--   liftIO $ TLIO.putStrLn greeting
--
-- main :: IO ()
-- main = do
--   translations <- locateTranslations $ linuxLocation "hello"
--   runMyMonad translations hello
--
-- @
--
-- See also working examples under @examples/@ directory.
--

-- | Look up for translation. Returns source string if translation not found.
lookup :: Translations -> LanguageId -> TranslationSource -> T.Text
lookup t lang bstr =
  case M.lookup lang (tMap t) of
    Nothing -> toText bstr
    Just gmo -> Gettext.gettext gmo bstr

-- | Execute function depending on current translation catalog and context.
withTranslation :: Localized m
                => (b -> r)    -- ^ Function to be executed if there is no
                               --   translation for current language loaded.
                -> (Gettext.Catalog -> Maybe Context -> b -> r) -- ^ Function to be executed on current catalog.
                -> (b -> m r)  -- ^ Function lifted into @Localized@ monad.
withTranslation dflt fn b = do
  t <- getTranslations
  lang <- getLanguage
  ctxt <- getContext
  case M.lookup lang (tMap t) of
    Nothing -> return $ dflt b
    Just gmo -> return $ fn gmo ctxt b

-- | Translate a string.
translate :: (Localized m) => TranslationSource -> m T.Text
translate orig = do
  t <- getTranslations
  lang <- getLanguage
  mbContext <- getContext
  case M.lookup lang (tMap t) of
    Nothing -> return $ toText orig
    Just gmo ->
      case mbContext of
        Nothing -> return $ Gettext.gettext gmo orig
        Just ctxt -> return $ Gettext.cgettext gmo ctxt orig

-- | Short alias for @translate@.
__ ::  (Localized m) => TranslationSource -> m T.Text
__ = translate

-- | Translate a string, taking plural forms into account.
translateN :: (Localized m)
           => TranslationSource -- ^ Single form in original language
           -> TranslationSource -- ^ Plural form in original language
           -> Int               -- ^ Number
           -> m T.Text
translateN orig plural n = do
  t <- getTranslations
  lang <- getLanguage
  mbContext <- getContext
  case M.lookup lang (tMap t) of
    Nothing -> return $ toText orig
    Just gmo ->
      case mbContext of
        Nothing -> return $ Gettext.ngettext gmo orig plural n
        Just ctxt -> return $ Gettext.cngettext gmo ctxt orig plural n

-- | Translate a string and substitute variables into it.
-- Data.Text.Format.Heavy.format syntax is used.
translateFormat :: (Localized m, F.VarContainer vars)
                => TranslationSource -- ^ Original formatting string
                -> vars              -- ^ Substitution variables
                -> m T.Text
translateFormat orig vars = do
  fmtStr <- translate orig
  case parseFormat fmtStr of
    Left err -> fail $ show err
    Right fmt -> return $ F.format fmt vars

-- | Short alias for @translateFormat@.
__f ::  (Localized m, F.VarContainer c) => TranslationSource -> c -> m T.Text
__f = translateFormat

-- | Translate a string, taking plural forms into account,
-- and substitute variables into it.
-- Data.Text.Format.Heavy.format syntax is used.
translateNFormat :: (Localized m, F.VarContainer vars)
                 => TranslationSource -- ^ Single form of formatting string in original language
                 -> TranslationSource -- ^ Plural form of formatting string in original language
                 -> Int               -- ^ Number
                 -> vars              -- ^ Substitution variables
                 -> m T.Text
translateNFormat orig plural n vars = do
  fmtStr <- translateN orig plural n
  case parseFormat fmtStr of
    Left err -> fail $ show err
    Right fmt -> return $ F.format fmt vars

-- | Short alias for @translateNFormat@.
__n :: (Localized m, F.VarContainer c) => TranslationSource -> TranslationSource -> Int -> c -> m T.Text
__n = translateNFormat