package org.languagetool.rules.spelling.hunspell;

import org.languagetool.JLanguageTool;
import org.languagetool.broker.ResourceDataBroker;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.function.BiFunction;

public final class Hunspell {
  static class LanguageAndPath {
    private final Path dictionary;
    private final Path affix;
    LanguageAndPath(Path dictionary, Path affix) {
      this.dictionary = Objects.requireNonNull(dictionary);
      this.affix = Objects.requireNonNull(affix);
    }
    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      LanguageAndPath that = (LanguageAndPath) o;
      return Objects.equals(dictionary, that.dictionary) &&
          Objects.equals(affix, that.affix);
    }
    @Override
    public int hashCode() {
      return Objects.hash(dictionary, affix);
    }
  }

  private static final Map<LanguageAndPath, HunspellDictionary> map = new HashMap<>();
  private static BiFunction<Path, Path, HunspellDictionary> hunspellDictionaryFactory = DumontsHunspellDictionary::new;

  public static void setHunspellDictionaryFactory(BiFunction<Path, Path, HunspellDictionary> factory) {
    hunspellDictionaryFactory = factory;
  }

  public static synchronized HunspellDictionary getDictionary(Path dictionary, Path affix) {
    LanguageAndPath key = new LanguageAndPath(dictionary, affix);
    HunspellDictionary hunspell = map.get(key);
    if (hunspell != null && !hunspell.isClosed()) {
      return hunspell;
    }
    HunspellDictionary newHunspell = hunspellDictionaryFactory.apply(dictionary, affix);
    map.put(key, newHunspell);
    return newHunspell;
  }

  public static HunspellDictionary forDictionaryInResources(String language, String resourcePath) {
    try {
      ResourceDataBroker broker = JLanguageTool.getDataBroker();
      InputStream dictionaryStream = broker.getAsStream(resourcePath + language + ".dic");
      InputStream affixStream = broker.getAsStream(resourcePath + language + ".aff");
      if (dictionaryStream == null || affixStream == null) {
        throw new RuntimeException("Could not find dictionary for language \"" + language + "\" in classpath");
      }
      Path dictionary = Files.createTempFile(language, ".dic");
      Path affix = Files.createTempFile(language, ".aff");
      Files.copy(dictionaryStream, dictionary, StandardCopyOption.REPLACE_EXISTING);
      Files.copy(affixStream, affix, StandardCopyOption.REPLACE_EXISTING);
      return hunspellDictionaryFactory.apply(dictionary, affix);
    } catch (IOException e) {
      throw new RuntimeException("Could not create temporary dictionaries for language \"" + language + "\"", e);
    }
  }

  public static HunspellDictionary forDictionaryInResources(String language) {
    return forDictionaryInResources(language, "");
  }
}
