/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool 
          with fuzzy matching, translation memory, keyword search, 
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2012 Alex Buloichik, Didier Briel, Thomas Cordonnier
               2016 Thomas Cordonnier
               2017 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 OmegaT is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.core.machinetranslators.net;

import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.omegat.core.machinetranslators.CachedTranslate;
import org.omegat.util.Language;
import org.omegat.util.Log;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;
import org.omegat.util.WikiGet;

/**
 * Support of Microsoft Translator machine translation.
 * 
 * http://www.microsofttranslator.com/dev/
 * http://msdn.microsoft.com/en-us/library/ff512421.aspx
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Didier Briel
 * @author Thomas Cordonnier
 */
public class MicrosoftTranslate extends CachedTranslate {
    protected static final String URL_TOKEN = "https://datamarket.accesscontrol.windows.net/v2/OAuth2-13/";
    protected static final String URL_TRANSLATE = "http://api.microsofttranslator.com/v2/Http.svc/Translate";
    protected static final Pattern RE_RESPONSE = Pattern.compile("<string.+?>(.+)</string>");

    protected String accessToken;

    @Override
    protected String getPreferenceName() {
        return Preferences.ALLOW_MICROSOFT_TRANSLATE;
    }

    public String getName() {
        return OStrings.getString("MT_ENGINE_MICROSOFT");
    }

    @Override
    protected synchronized String translate(Language sLang, Language tLang, String text) throws Exception {
        text = text.length() > 10000 ? text.substring(0, 9997) + "..." : text;

        // Note : if there are exceptions, propagate to the requester : 
        // 	error message and translation string are two different things!!!
        String langFrom = checkMSLang(sLang);
        String langTo = checkMSLang(tLang);
        String translation;
        if (accessToken == null) {
            requestToken();
            translation = requestTranslate(langFrom, langTo, text);
        } else {
            try {
                translation = requestTranslate(langFrom, langTo, text);
            } catch (WikiGet.ResponseError ex) {
                if (ex.code == 400) {
                    requestToken();
                    translation = requestTranslate(langFrom, langTo, text);
                } else {
                    throw ex;
                }
            }
        }
        return translation;
    }

    /**
     * Converts language codes to Microsoft ones.
     * @param language
     *              a project language
     * @return either a language code, or a Chinese language code plus a Microsoft variant
     */
    private String checkMSLang(Language language) {
        if (language.getLanguage().compareToIgnoreCase("zh-cn") == 0){
            return "zh-CHS";
        } else if ((language.getLanguage().compareToIgnoreCase("zh-tw") == 0) ||
                   (language.getLanguage().compareToIgnoreCase("zh-hk") == 0)) {
            return "zh-CHT";
        } else {
            return language.getLanguageCode();         
        }
        
    }
    private void requestToken() throws Exception {
        Map<String, String> p = new TreeMap<String, String>();

        if (System.getProperty("microsoft.api.client_id") == null
                || System.getProperty("microsoft.api.client_secret") == null) {
            throw new KeyException(OStrings.getString("MT_ENGINE_MICROSOFT_KEY_NOTFOUND"));
        }

        p.put("client_id", System.getProperty("microsoft.api.client_id"));
        p.put("client_secret", System.getProperty("microsoft.api.client_secret"));
        p.put("scope", "http://api.microsofttranslator.com");
        p.put("grant_type", "client_credentials");
        String r = WikiGet.post(URL_TOKEN, p, null);
        Map<String, String> rmap = unpackJson(r);
        accessToken = rmap.get("access_token");
    }

    private String requestTranslate(String langFrom, String langTo, String text) throws Exception {
        Map<String, String> p = new TreeMap<String, String>();
        p.put("appId", "Bearer " + accessToken);
        p.put("text", text);
        p.put("from", langFrom);
        p.put("to", langTo);
        p.put("contentType", "text/plain");

        String r = WikiGet.get(URL_TRANSLATE, p, null);
        Matcher m = RE_RESPONSE.matcher(r);
        if (m.matches()) {
            String translatedText = m.group(1);
            translatedText = translatedText.replace("&lt;", "<");
            translatedText = translatedText.replace("&gt;", ">");
            return translatedText;
        } else {
            Log.logWarningRB("MT_ENGINE_MICROSOFT_WRONG_RESPONSE");
            return null;
        }
    }

    int pos;
    String str;

    public Map<String, String> unpackJson(String v) {
        if (!v.startsWith("{") || !v.endsWith("}")) {
            throw new RuntimeException("Wrong");
        }
        str = v.substring(v.offsetByCodePoints(0, 1),
                v.offsetByCodePoints(v.length(), -1));
        pos = 0;

        Map<String, String> result = new TreeMap<String, String>();

        while (true) {
            String key = readString();
            mustBe(':');
            String value = readString();
            result.put(key, value);
            if (pos == str.length()) {
                break;
            }
            mustBe(',');
        }
     
        return result;
    }

    String readString() {
        if (str.codePointAt(pos) != '"') {
            throw new RuntimeException("Wrong");
        }
        int startOffset = str.offsetByCodePoints(pos, 1);
        int endOffset = str.indexOf('"', startOffset);
        if (endOffset < 0) {
            throw new RuntimeException("Wrong");
        }
        String result = str.substring(startOffset, endOffset);
        pos = str.offsetByCodePoints(endOffset, 1);
        return result;
    }

    void mustBe(int c) {
        int cp = str.codePointAt(pos);
        if (cp != c) {
            throw new RuntimeException("Wrong");
        }
        pos += Character.charCount(cp);
    }
}
