/* LanguageTool plugin for Oxygen XML editor 
 * Copyright (C) 2014 Daniel Naber (http://www.danielnaber.de)
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package org.languagetool.oxygen;

import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Access to a LanguageTool instance via HTTP.
 */
class LanguageToolClient {

  private final String serverUrl;

  LanguageToolClient(String serverUrl) {
    this.serverUrl = serverUrl;
  }

  List<RuleMatch> checkText(TextWithMapping text, String langCode) {
    HttpURLConnection connection = null;
    try {
      String urlParameters =
              "language=" + langCode.replace('_', '-') +
              "&text=" + URLEncoder.encode(text.getText(), "utf-8");
      URL languageToolUrl = new URL(serverUrl);
      connection = openConnection(languageToolUrl);
      writeParameters(urlParameters, connection);
      InputStream inputStream = connection.getInputStream();
      return parseXml(inputStream, text);
    } catch (MappingException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(
              "Could not check text using LanguageTool server at URL '" + serverUrl + "':\n" +
              e.getMessage() + "\n" +
              "Please make sure the LanguageTool server is running at this URL or\n" +
              "change the location at Options -> Preferences... -> Plugins.", e);
    } finally {
      if (connection != null) {
        connection.disconnect();
      }
    }
  }

  private HttpURLConnection openConnection(URL languageToolUrl) throws IOException {
    HttpURLConnection connection = (HttpURLConnection) languageToolUrl.openConnection();
    connection.setDoOutput(true);
    connection.setDoInput(true);
    connection.setInstanceFollowRedirects(false);
    connection.setRequestMethod("POST");
    connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
    connection.setRequestProperty("charset", "utf-8");
    connection.setUseCaches(false);
    return connection;
  }

  private void writeParameters(String urlParameters, HttpURLConnection connection) throws IOException {
    DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
    try {
      wr.write(urlParameters.getBytes("UTF-8"));
    } finally {
      wr.close();
    }
  }

  private List<RuleMatch> parseXml(InputStream xml, TextWithMapping text) throws XPathExpressionException {
    XPath xPath = XPathFactory.newInstance().newXPath();
    Document document = XmlTools.getDocument(xml);
    NodeList nodeSet = (NodeList) xPath.evaluate("//error", document, XPathConstants.NODESET);
    List<RuleMatch> matches = new ArrayList<RuleMatch>();
    for (int i = 0; i < nodeSet.getLength(); i++) {
      Node errorNode = nodeSet.item(i);
      RuleMatch ruleMatch = getRuleMatch(text, errorNode);
      matches.add(ruleMatch);
    }
    return matches;
  }

  private RuleMatch getRuleMatch(TextWithMapping text, Node errorNode) {
    NamedNodeMap attributes = errorNode.getAttributes();
    String message = attributes.getNamedItem("msg").getNodeValue();
    Node replacementAttribute = attributes.getNamedItem("replacements");
    List<String> replacements = replacementAttribute != null ?
            Arrays.asList(replacementAttribute.getNodeValue().split("#")) : Collections.<String>emptyList();
    String offsetStr = attributes.getNamedItem("offset").getNodeValue();
    String lengthStr = attributes.getNamedItem("errorlength").getNodeValue();
    Node issueType = attributes.getNamedItem("locqualityissuetype");
    int offset = Integer.parseInt(offsetStr);
    int length = Integer.parseInt(lengthStr);
    RuleMatch ruleMatch = new RuleMatch(message, offset, offset + length, replacements, issueType != null ? issueType.getNodeValue() : null);
    try {
      ruleMatch.setOxygenOffsetStart(text.getOxygenPositionFor(offset) + 1);
      ruleMatch.setOxygenOffsetEnd(text.getOxygenPositionFor(offset + length - 1) + 1);
    } catch (Exception e) {
      throw new MappingException("Could not map start or end offset of rule match '" +
              ruleMatch.getMessage() + "': " + offset + "-" + (offset + length - 1), e);
    }
    return ruleMatch;
  }

}

@@ -998,16 +998,75 @@
 odeList;
+%0Aimport ro.sync.net.protocol.http.HttpExceptionWithDetails;
 %0A%0Aimport
@@ -2197,16 +2197,601 @@
 hrow e;%0A
+    %7D catch (HttpExceptionWithDetails e) %7B%0A      String reason = e.getReason();%0A      System.err.println(e.getMessage() + %22: %22 + reason);%0A      if (reason.length() %3E 80) %7B%0A        reason = reason.substring(0, 80) + %22...%22;%0A      %7D%0A      throw new RuntimeException(%22Could not check text using LanguageTool server at URL '%22 + serverUrl + %22':%5Cn%5Cn%22 +%0A              e.getMessage() + %22%5Cn%22 +%0A              reason + %22%5Cn%5Cn%22 +%0A              %22Please make sure the LanguageTool server is running at this URL or%5Cn%22 +%0A              %22change the location at Options -%3E Preferences... -%3E Plugins.%22, e);%0A
     %7D ca
@@ -2935,16 +2935,18 @@
  + %22':%5Cn
+%5Cn
 %22 +%0A    
@@ -2967,32 +2967,34 @@
 tMessage() + %22%5Cn
+%5Cn
 %22 +%0A            

improve error message for cases where language is not supported