/* LanguageTool, a natural language style checker 
 * Copyright (C) 2023 Jaume Ortolà
 * 
 * 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.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

import org.languagetool.AnalyzedTokenReadings;
import org.languagetool.Language;
import org.languagetool.Languages;
import org.languagetool.AnalyzedToken;
import org.languagetool.rules.RuleMatch;
import org.languagetool.rules.patterns.RuleFilter;
import org.languagetool.rules.patterns.PatternRule;
import org.languagetool.synthesis.ca.CatalanSynthesizer;
import org.languagetool.tools.StringTools;

/*
 * Suggestions for rule OBLIDARSE: se m'ha oblidat -> me n'he oblidat, m'he oblidat de
 */

public class OblidarseSugestionsFilter extends RuleFilter {

  static private CatalanSynthesizer synth = CatalanSynthesizer.INSTANCE;
  Language lang = Languages.getLanguageForShortCode("ca");

  Pattern pApostropheNeeded = Pattern.compile("h?[aeiouàèéíòóú].*", Pattern.CASE_INSENSITIVE);

  private static Map<String, String> addReflexiveVowel = new HashMap<>();
  static {
    addReflexiveVowel.put("1S", "m'");
    addReflexiveVowel.put("2S", "t'");
    addReflexiveVowel.put("3S", "s'");
    addReflexiveVowel.put("1P", "ens ");
    addReflexiveVowel.put("2P", "us ");
    addReflexiveVowel.put("3P", "s'");
  }

  private static Map<String, String> addReflexiveConsonant = new HashMap<>();
  static {
    addReflexiveConsonant.put("1S", "em ");
    addReflexiveConsonant.put("2S", "et ");
    addReflexiveConsonant.put("3S", "es ");
    addReflexiveConsonant.put("1P", "ens ");
    addReflexiveConsonant.put("2P", "us ");
    addReflexiveConsonant.put("3P", "es ");
  }

  private static Map<String, String> addReflexiveEnVowel = new HashMap<>();
  static {
    addReflexiveEnVowel.put("1S", "me n'");
    addReflexiveEnVowel.put("2S", "te n'");
    addReflexiveEnVowel.put("3S", "se n'");
    addReflexiveEnVowel.put("1P", "ens n'");
    addReflexiveEnVowel.put("2P", "us n'");
    addReflexiveEnVowel.put("3P", "se n'");
  }

  private static Map<String, String> addReflexiveEnConsonant = new HashMap<>();
  static {
    addReflexiveEnConsonant.put("1S", "me'n ");
    addReflexiveEnConsonant.put("2S", "te'n ");
    addReflexiveEnConsonant.put("3S", "se'n ");
    addReflexiveEnConsonant.put("1P", "ens en ");
    addReflexiveEnConsonant.put("2P", "us en ");
    addReflexiveEnConsonant.put("3P", "se'n ");
  }

  @Override
  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String, String> arguments, int patternTokenPos,
      AnalyzedTokenReadings[] patternTokens) throws IOException {
    int posWord = 0;
    AnalyzedTokenReadings[] tokens = match.getSentence().getTokensWithoutWhitespace();
    while (posWord < tokens.length
        && (tokens[posWord].getStartPos() < match.getFromPos() || tokens[posWord].isSentenceStart())) {
      posWord++;
    }
    String pronomPostag = tokens[posWord + 1].readingWithTagRegex("P.*").getPOSTag();
    String pronomGenderNumber = pronomPostag.substring(2, 3) + pronomPostag.substring(4, 5);
    int indexMainVerb = posWord + 2;
    while (!tokens[indexMainVerb].hasAnyLemma("oblidar", "descuidar", "passar")) {
      indexMainVerb++;
    }
    String verbPostag = tokens[posWord + 2].readingWithTagRegex("V.*").getPOSTag();
    String lemma = tokens[posWord + 2].readingWithTagRegex("V.*").getLemma();
    if (lemma.equals("passar")) {
      lemma = "descuidar";
    }
    AnalyzedToken at = new AnalyzedToken("", "", lemma);
    String[] synthForms = synth.synthesize(at,
        verbPostag.substring(0, 4) + pronomGenderNumber + verbPostag.substring(6, 8), getLanguageVariantCode(match));
    String newVerb = "";
    if (synthForms.length == 0) {
      return null;
    }
    newVerb = synthForms[0];
    for (int i = posWord + 3; i < indexMainVerb + 1; i++) {
      newVerb = newVerb + tokens[i].getWhitespaceBefore() + tokens[i].getToken().replace("passar", "descuidar")
          .replace("passat", "descuidat").replace("passant", "descuidant");
    }
    boolean verbVowel = pApostropheNeeded.matcher(newVerb).matches();
    String wordAfter = "";

    if (indexMainVerb + 1 < tokens.length) {
      AnalyzedToken wordAfterReading = tokens[indexMainVerb + 1].readingWithTagRegex("D.*|V.N.*|P[DI].*|NC.*");
      if (wordAfterReading != null) {
        wordAfter = wordAfterReading.getToken();
      }
      List<String> exceptionsList = Arrays.asList("com", "de", "d'");
      if (exceptionsList.contains(tokens[indexMainVerb + 1].getToken().toLowerCase())) {
        wordAfter = tokens[indexMainVerb + 1].getToken();
      }
    }
    Map<String, String> transform;
    if (wordAfter.isEmpty() && !wordAfter.equalsIgnoreCase("de") && !wordAfter.equalsIgnoreCase("d'")) {
      transform = (verbVowel ? addReflexiveEnVowel : addReflexiveEnConsonant);
    } else {
      transform = (verbVowel ? addReflexiveVowel : addReflexiveConsonant);
    }
    StringBuilder suggBld = new StringBuilder();
    suggBld.append(transform.get(pronomGenderNumber));
    suggBld.append(newVerb);
    boolean wordAfterApostrophe = false;
    int charactersAfterCorrection = 0;
    // contracció de + el -> del
    if (wordAfter.equalsIgnoreCase("el") || wordAfter.equalsIgnoreCase("els")) {
      suggBld.append(" d");
      suggBld.append(wordAfter.toLowerCase());
      charactersAfterCorrection = wordAfter.length() + 1;
      // apostrofació: de | d'
    } else if (!wordAfter.isEmpty() && !wordAfter.equalsIgnoreCase("de") && !wordAfter.equalsIgnoreCase("d'")) {
      wordAfterApostrophe = pApostropheNeeded.matcher(wordAfter).matches();
      suggBld.append(wordAfterApostrophe ? " d'" : " de");
      charactersAfterCorrection = (wordAfterApostrophe ? 1 : 0);
    }
    String replacement = StringTools.preserveCase(suggBld.toString(), tokens[posWord].getToken());
    List<String> replacements = new ArrayList<>();
    replacements.add(replacement);
    for (String s : match.getSuggestedReplacements()) {
      if (charactersAfterCorrection == 1) {
        s = s + " ";
      }
      replacements.add(lang.adaptSuggestion(s));
    }
    if (replacements.isEmpty()) {
      return null;
    }
    RuleMatch ruleMatch = new RuleMatch(match.getRule(), match.getSentence(), tokens[posWord].getStartPos(),
        tokens[indexMainVerb].getEndPos() + charactersAfterCorrection,
        match.getMessage().replace("passar", "descuidar"), match.getShortMessage());
    ruleMatch.setType(match.getType());
    ruleMatch.setSuggestedReplacements(replacements);
    return ruleMatch;
  }

  private String getLanguageVariantCode(RuleMatch match) {
    PatternRule pr = (PatternRule) match.getRule();
    return pr.getLanguage().getShortCodeWithCountryAndVariant();
  }
}