{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}
module NicovideoTranslator.Main (main) where

import Data.Char (toLower)
import System.IO (stderr, stdout)
import System.IO.Error (catchIOError)

import Data.Data (Data)
import qualified Data.LanguageCodes as L
import qualified Data.Text as T
import Data.Text.Encoding (encodeUtf8)
import Data.Text.Format (Format, format)
import Data.Text.Format.Params (Params)
import Data.Text.Format.Types (Only(Only))
import Data.Text.IO (hPutStrLn)
import qualified Data.Text.Lazy as LT
import Data.Typeable (Typeable)
import Network.DNS.Resolver (defaultResolvConf, makeResolvSeed, withResolver)
import Network.DNS.Lookup (lookupA)
import Network.Wai.Handler.Warp (Port, run)
import System.Console.CmdArgs ( argPos
                              , cmdArgs
                              , def
                              , details
                              , explicit
                              , help
                              , name
                              , program
                              , summary
                              , typ
                              , (&=)
                              )
import System.Locale.SetLocale (Category(LC_CTYPE), setLocale)

import NicovideoTranslator.Proxy ( ProxyConfiguration ( ProxyConfiguration
                                                      , apiKey
                                                      , language
                                                      , upstreamHost
                                                      )
                                 , app
                                 )

data Translator = Translator { port :: Port
                             , language' :: String
                             , apiKey' :: String
                             } deriving (Show, Data, Typeable)

formatIoError :: Params ps => Format -> ps -> IO a
formatIoError fmt ps = ioError $ userError $ LT.unpack $ format fmt ps 

readLanguageCode :: [Char] -> IO L.ISO639_1
readLanguageCode (x:y:[]) =
    case L.fromChars (toLower x) (toLower y) of
        Just lang -> return lang
        _ -> formatIoError "{} is wrong language code" (Only (x:y:[]))
readLanguageCode lang = formatIoError "{} is wrong language code" (Only lang)

currentLanguage :: IO L.ISO639_1
currentLanguage = do
    currentLocale <- setLocale LC_CTYPE Nothing
    case currentLocale of
        Just s -> readLanguageCode s
        Nothing -> formatIoError "locale is not set" ()

defaultUpstreamHost :: T.Text
defaultUpstreamHost = "nmsg.nicovideo.jp"

translateCmdArgs :: String -> Translator
translateCmdArgs lang =
    Translator { language' = lang &= explicit
                                  &= name "language"
                                  &= name "lang"
                                  &= name "l"
                                  &= typ "LANG"
                                  &= help "Target language to translate to [en]"
               , port = 80 &= typ "PORT"
                           &= help "Port number to listen [80]"
               , apiKey' = def &= argPos 0 &= typ "API_KEY"
               }
        &= program "nicovideo-translator"
        &= summary "Nico Nico Douga (ニコニコ動画) Comment Translator"
        &= details
            ["It takes a Google Translate API key as its first argument."]

main :: IO ()
main = do
    currentLang <- catchIOError currentLanguage (\_ -> return L.EN)
    opts <- cmdArgs $ translateCmdArgs $ L.language currentLang
    lang <- readLanguageCode $ language' opts
    let portNum = port opts
    hPutStrLn stdout $ LT.toStrict $
        format "Running on http://0.0.0.0:{}/ (Press ^C to quit)" (Only portNum)
    rs <- makeResolvSeed defaultResolvConf
    resolution <- withResolver rs $
        \resolver -> lookupA resolver (encodeUtf8 defaultUpstreamHost)
    case resolution of
        Right (resolvedHost:_) ->
            let upstreamHost' = T.pack $ show resolvedHost
            in do
                hPutStrLn stdout $ T.concat ["Upstream: "
                                            , defaultUpstreamHost
                                            , " (", upstreamHost', ")"
                                            ]
                let conf = ProxyConfiguration { language = lang
                                              , upstreamHost = upstreamHost'
                                              , apiKey = T.pack $ apiKey' opts
                                              }
                run portNum $ app conf
        _ -> hPutStrLn stderr $ T.concat [ "error: failed to resolve "
                                         , defaultUpstreamHost
                                         ]