module Happstack.Server.I18N
     ( acceptLanguage
     , bestLanguage
     ) where

import Control.Arrow ((>>>), first, second)
import Data.Function (on)
import qualified Data.ByteString.Char8 as C
import Data.List     (sortBy)
import Data.Maybe    (fromMaybe)
import Data.Text     as Text (Text, breakOnAll, pack, singleton)
import Happstack.Server.Monads (Happstack, getHeaderM)
import Happstack.Server.Internal.Compression (encodings)
import Text.ParserCombinators.Parsec (parse)

-- TODO: proper Accept-Language parser

-- | parse the 'Accept-Language' header, or [] if not found.
acceptLanguage :: (Happstack m) => m [(Text, Maybe Double)]
acceptLanguage =
    do mAcceptLanguage <- (fmap C.unpack) <$> getHeaderM "Accept-Language"
       case mAcceptLanguage of
         Nothing   -> return []
         (Just al) ->
             case parse encodings al al of
               (Left _) -> return []
               (Right encs) -> return (map (first Text.pack) encs)

-- | deconstruct the 'acceptLanguage' results a return a list of
-- languages sorted by preference in descending order.
--
-- Note: this implementation does not conform to RFC4647
--
-- Among other things, it does not handle wildcards. A proper
-- implementation needs to take a list of available languages.
bestLanguage :: [(Text, Maybe Double)] -> [Text]
bestLanguage range =
    -- is no 'q' param, set 'q' to 1.0
    map (second $ fromMaybe 1)     >>>
    -- sort in descending order
    sortBy (flip compare `on` snd) >>>
    -- remove entries with '*' or q == 0. Removing '*' entries is not
    -- technically correct, but it is the best we can do with out a
    -- list of available languages.
    filter (\(lang, q) -> lang /= (Text.singleton '*') && q > 0)  >>>
    -- lookup fallback (RFC 4647, Section 3.4)
    concatMap (explode . fst) $
    range
    where
      -- | example: "en-us-gb" -> ["en-us-gb","en-us","en"]
      explode :: Text -> [Text]
      explode lang = lang : (reverse $ map fst $ breakOnAll (singleton '-') lang)