// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2014 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0

package com.google.appinventor.components.runtime;

import android.app.Activity;
import com.google.appinventor.components.annotations.UsesPermissions;
import org.json.JSONException;
import org.json.JSONObject;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.common.YaVersion;
import com.google.appinventor.components.runtime.util.AsynchUtil;
import com.google.appinventor.components.runtime.util.ErrorMessages;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

@DesignerComponent(version = YaVersion.YANDEX_COMPONENT_VERSION,
    description = "Use this component to translate words and sentences between different " +
        "languages. This component needs Internet access, as it will request " +
        "translations to the Yandex.Translate service. Specify the source and " +
        "target language in the form source-target using two letter language " +
        "codes. So\"en-es\" will translate from English to Spanish while " +
        "\"es-ru\" will translate from Spanish to Russian. If you leave " +
        "out the source language, the service will attempt to detect the source " +
        "language. So providing just \"es\" will attempt to detect the source " +
        "language and translate it to Spanish.<p /> This component is powered by the " +
        "Yandex translation service.  See http://api.yandex.com/translate/ " +
        "for more information, including the list of available languages and the " +
        "meanings of the language codes and status codes. " +
        "<p />Note: Translation happens asynchronously in the background. When the " +
        "translation is complete, the \"GotTranslation\" event is triggered.",
    category = ComponentCategory.MEDIA,
    nonVisible = true,
    iconName = "images/yandex.png")
@UsesPermissions(permissionNames = "android.permission.INTERNET")
@SimpleObject
public final class YandexTranslate extends AndroidNonvisibleComponent {

  public static final String YANDEX_TRANSLATE_SERVICE_URL =
      "https://translate.yandex.net/api/v1.5/tr.json/translate?key=";
  private final String yandexKey;
  private final Activity activity;

  /**
   * Creates a new component.
   *
   * @param container  container, component will be placed in
   */
  public YandexTranslate(ComponentContainer container) {
    super(container.$form());

    // Set up the Yandex.Translate Tagline in the 'About' screen
    form.setYandexTranslateTagline();

    // TODO (user) To provide users with this component you will need to obtain a key with the
    // Yandex.Translate service at http://api.yandex.com/translate/
    yandexKey = "";
    activity = container.$context();
  }

  /**
   * Performs an HTTP GET request to the Yandex.Translate service
   */
  @SimpleFunction(description = "By providing a target language to translate to (for instance, " +
      "'es' for Spanish, 'en' for English, or 'ru' for Russian), and a word or sentence to " +
      "translate, this method will request a translation to the Yandex.Translate service.\n" +
      "Once the text is translated by the external service, the event GotTranslation will be " +
      "executed.\nNote: Yandex.Translate will attempt to detect the source language. You can " +
      "also specify prepending it to the language translation. I.e., es-ru will specify Spanish " +
      "to Russian translation.")
  public void RequestTranslation(final String languageToTranslateTo,
                                 final String textToTranslate) {

    if (yandexKey.equals("")){
      form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
          ErrorMessages.ERROR_TRANSLATE_NO_KEY_FOUND);
      return;
    }

    AsynchUtil.runAsynchronously(new Runnable() {
      @Override
      public void run() {
        try {
          performRequest(languageToTranslateTo, textToTranslate);
        } catch (IOException e) {
          form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
              ErrorMessages.ERROR_TRANSLATE_SERVICE_NOT_AVAILABLE);
        } catch (JSONException je) {
          form.dispatchErrorOccurredEvent(YandexTranslate.this, "RequestTranslation",
              ErrorMessages.ERROR_TRANSLATE_JSON_RESPONSE);
        }
      }
    });
  }

  /**
   * This is the actual request to the Yandex.Translate service. It opens a https connection to
   * their web based API and parses the JSON response.
   * @param languageToTranslateTo the language that the text will be translated into.
   * @param textToTranslate the word or sentence to translate.
   * @throws IOException if the connection is not successful.
   * @throws JSONException if the JSON response cannot be parsed.
   */
  private void performRequest(String languageToTranslateTo, String textToTranslate)
      throws IOException, JSONException {

    final String finalURL = YANDEX_TRANSLATE_SERVICE_URL +
        this.yandexKey +
        "&lang=" + languageToTranslateTo +
        "&text=" + URLEncoder.encode(textToTranslate, "UTF-8");

    URL url = new URL(finalURL);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    if (connection != null) {
      try {
        final String responseContent = getResponseContent(connection);

        JSONObject jsonResponse = new JSONObject(responseContent);

        final String responseCode = jsonResponse.getString("code");

        // The translation will be in position Zero of the array returned with key 'text'
        org.json.JSONArray response = jsonResponse.getJSONArray("text");
        final String translation = (String)response.get(0);

        // Dispatch the event.
        activity.runOnUiThread(new Runnable() {
          @Override
          public void run() {
            GotTranslation(responseCode, translation);
          }
        });

      } finally {
        connection.disconnect();
      }
    }
  }

  /**
   * This method reads from a stream based on the passed connection
   * @param connection the connection to read from
   * @return the contents of the stream
   * @throws IOException if it cannot read from the http connection
   */
  private static String getResponseContent(HttpURLConnection connection) throws IOException {
    // Use the content encoding to convert bytes to characters.
    String encoding = connection.getContentEncoding();
    if (encoding == null) {
      encoding = "UTF-8";
    }
    InputStreamReader reader = new InputStreamReader(connection.getInputStream(), encoding);
    try {
      int contentLength = connection.getContentLength();
      StringBuilder sb = (contentLength != -1)
          ? new StringBuilder(contentLength)
          : new StringBuilder();
      char[] buf = new char[1024];
      int read;
      while ((read = reader.read(buf)) != -1) {
        sb.append(buf, 0, read);
      }
      return sb.toString();
    } finally {
      reader.close();
    }
  }

  /**
   * Event indicating that a request has finished and has returned data (translation).
   *
   * @param responseCode the response code from the server
   * @param translation the response content from the server
   */
  @SimpleEvent(description = "Event triggered when the Yandex.Translate service returns the " +
      "translated text. This event also provides a response code for error handling. If the " +
      "responseCode is not 200, then something went wrong with the call, and the translation will " +
      "not be available.")
  public void GotTranslation(String responseCode, String translation) {
    EventDispatcher.dispatchEvent(this, "GotTranslation", responseCode, translation);
  }

}