/* LanguageTool, a natural language style checker 
 * Copyright (C) 2005 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.rules.ca;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.languagetool.AnalyzedSentence;
import org.languagetool.AnalyzedToken;
import org.languagetool.AnalyzedTokenReadings;
import org.languagetool.Language;
import org.languagetool.rules.*;
import org.languagetool.synthesis.ca.CatalanSynthesizer;

/**
 * A rule that suggests better names for technical operation names
 * 
 * Loads the relevant words from <code>/ca/replace_operationnames.txt</code>.
 * 
 * @author Jaume Ortolà
 */
public class ReplaceOperationNamesRule extends AbstractSimpleReplaceRule {

  private static final Map<String, List<String>> wrongWords = loadFromPath("/ca/replace_operationnames.txt");
  private static final Locale CA_LOCALE = new Locale("CA");

  @Override
  protected Map<String, List<String>> getWrongWords() {
    return wrongWords;
  }
  
  private CatalanSynthesizer synth;
  
  private static final Pattern PrevToken_POS = Pattern.compile("D[^R].*|PX.*|SPS00|SENT_START");
  private static final Pattern PrevToken_POS_Excep = Pattern.compile("RG_anteposat|N.*|CC|_PUNCT.*|_loc_unavegada|RN");
  private static final Pattern NextToken_POS_Excep = Pattern.compile("N.*");
  
  private static final Pattern PUNTUACIO = Pattern.compile("PUNCT.*|SENT_START");
  private static final Pattern DETERMINANT = Pattern.compile("D[^R].M.*");
  

  public ReplaceOperationNamesRule(final ResourceBundle messages, Language language) throws IOException {
    super(messages);
    super.setLocQualityIssueType(ITSIssueType.Style);
    super.setCategory(new Category(new CategoryId("FORMES_SECUNDARIES"), "C8) Formes secundàries")); 
    synth = (CatalanSynthesizer) language.getSynthesizer();
  }  

  @Override
  public final String getId() {
    return "NOMS_OPERACIONS";
  }

 @Override
  public String getDescription() {
    return "Noms d'operació tècnica: buidat/buidatge";
  }

  @Override
  public String getShort() {
    return "Forma preferible";
  }
  
  @Override
  public String getMessage(String tokenStr, List<String> replacements) {
    return "Si és el nom d'una operació tècnica, val més usar una altra forma.";
  }

  @Override
  public Locale getLocale() {
    return CA_LOCALE;
  }
  
  @Override
  public final RuleMatch[] match(final AnalyzedSentence sentence) {
    List<RuleMatch> ruleMatches = new ArrayList<>();
    AnalyzedTokenReadings[] tokens = sentence.getTokensWithoutWhitespace();

    loop: for (int i=1; i<tokens.length; i++) {

      List<String> replacementLemmas = null; 

      String token = tokens[i].getToken().toLowerCase();
      
      if (token.length()>3 && token.endsWith("s")) {
        token = token.substring(0, token.length() - 1);
      }
      if (wrongWords.containsKey(token)) {
        replacementLemmas = wrongWords.get(token);
      } else {
        continue loop;
      }
      
      // exceptions
      if (token.equals("duplicat") && tokens[i-1].getToken().equalsIgnoreCase("per")) {
        continue loop;
      }
      // Assecat el braç del riu
      if (i + 1 < tokens.length &&
          matchPostagRegexp(tokens[i - 1], PUNTUACIO) &&
          matchPostagRegexp(tokens[i + 1], DETERMINANT)) {
        continue loop;
      }  
      
      // relevant token
      if (tokens[i].hasPosTag("_GV_")) {
        continue loop;
      }
  
      // next token
      if (i + 1 < tokens.length
          && (tokens[i + 1].hasLemma("per") || tokens[i + 1].hasLemma("com")
              || tokens[i + 1].hasLemma("des") || tokens[i + 1].hasLemma("amb") 
              || matchPostagRegexp(tokens[i + 1], NextToken_POS_Excep))) {
        continue loop;
      }
      
      // prev token
      if (!matchPostagRegexp(tokens[i - 1], PrevToken_POS)
          || matchPostagRegexp(tokens[i - 1], PrevToken_POS_Excep)) {
        continue loop;
      }
      
      // The rule matches!
      //synthesize replacements
      
      if (replacementLemmas != null) {
        List<String> possibleReplacements = new ArrayList<>();
        String[] synthesized = null;

        if (!tokens[i].getToken().toLowerCase().endsWith("s")) {
          possibleReplacements.addAll(replacementLemmas);
        } else { 
          //synthesize plural
          for (String replacementLemma : replacementLemmas) {
            try {
              synthesized = synth.synthesize(new AnalyzedToken(
                  replacementLemma, "NCMS000", replacementLemma), "NC.P.*");
            } catch (IOException e) {
              throw new RuntimeException("Could not synthesize: "
                  + replacementLemma + " with tag NC.P.*.", e);
            }
            possibleReplacements.addAll(Arrays.asList(synthesized));
          }
        }
        if (possibleReplacements.size() > 0) {
            RuleMatch potentialRuleMatch = createRuleMatch(tokens[i],possibleReplacements, sentence);
            ruleMatches.add(potentialRuleMatch);
        }
      }
    }
    return toRuleMatchArray(ruleMatches);
  }
  
  /**
   * Match POS tag with regular expression
   */
  private boolean matchPostagRegexp(AnalyzedTokenReadings aToken, Pattern pattern) {
    boolean matches = false;
    for (AnalyzedToken analyzedToken : aToken) {
      String posTag = analyzedToken.getPOSTag();
      if (posTag == null) {
        posTag = "UNKNOWN";
      }
      final Matcher m = pattern.matcher(posTag);
      if (m.matches()) {
        matches = true;
        break;
      }
    }
    return matches;
  }



}
