
package de.ivu.elect.util;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;


import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.leftPad;
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
import static org.apache.commons.lang3.StringUtils.trimToEmpty;

public class FileUtil {

  private static final String DEFAULT_REPLACEMENT = "_";

  private static final String REG_EX_CHARACTERS_VALID = "a-zA-Z0-9\\-" + Pattern.quote(DEFAULT_REPLACEMENT);

  private static final String REG_EX_CHARACTERS_INVALID = "[^" + REG_EX_CHARACTERS_VALID + "]";

  private FileUtil() {
    super();
  }

  public static String normalizeFileName(String value) {
    return normalizeFileName(value, false);
  }

  public static String normalizeFileNamePreserveCase(String value) {
    return normalizeFileName(value, true);
  }

  public static String normalizeFileName(String value, boolean preserveCase) {
    String result = normalizeWhitespaces(value);

    if (preserveCase) {
      result = result.replace("Ä", "Ae");
      result = result.replace("Ö", "Oe");
      result = result.replace("Ü", "Ue");
    } else {
      result = result.toLowerCase();
    }
    result = result.replace("ä", "ae");
    result = result.replace("ö", "oe");
    result = result.replace("ü", "ue");
    result = result.replace("ß", "ss");

    // ELECTVAPP-681 Akzente entfernen, z.B. é -> e (siehe https://stackoverflow.com/a/8523728)
    result = Normalizer.normalize(result, Normalizer.Form.NFD).replaceAll("[^\\p{ASCII}]", "");

    result = result.replaceAll(REG_EX_CHARACTERS_INVALID, "");

    if (isBlank(result)) {
      return DEFAULT_REPLACEMENT;
    }

    return result;
  }

  public static String normalizeZipEntryNameOrDefaultByIndex(String value, int index) {
    return defaultIfBlank(normalizeZipEntryName(value), leftPad(String.valueOf(index), 3, "0"));
  }

  public static String normalizeZipEntryName(String value) {
    return normalizeZipEntryName(value, false);
  }

  public static String normalizeZipEntryNamePreserveCase(String value) {
    return normalizeZipEntryName(value, true);
  }

  private static String normalizeZipEntryName(String value, boolean preserveCase) {
    String result = trimToEmpty(value);

    // NOTE, dass ein Slash ('/') bei einem Zip-Entry als Ordner erkannt wird
    result = result.replaceAll("/", DEFAULT_REPLACEMENT);
    result = normalizeFileName(result, preserveCase);

    return result;
  }

  private static String normalizeWhitespaces(String value) {
    return normalizeSpace(trimToEmpty(value)).replace(" ", DEFAULT_REPLACEMENT);
  }

  // ELECTBB-341
  public static List<File> listFilesInDirectoryFast(File dir, boolean recursiv) throws IOException {
    return listFilesInDirectoryFast(dir, recursiv, Integer.MAX_VALUE);
  }

  public static List<File> listFilesInDirectoryFast(File dir, boolean recursiv, int maxFiles) throws IOException {
    List<File> result = new ArrayList<>(500/* willkürlich, maxFiles geht nicht, weil Integer.MAX_VALUE*/);

    Files.walkFileTree(dir.toPath(), new SimpleFileVisitor<Path>() {

      @Override
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
        result.add(file.toFile());
        if (result.size() > maxFiles) {
          return FileVisitResult.TERMINATE;
        }
        return super.visitFile(file, attrs);
      }

      @Override
      public FileVisitResult preVisitDirectory(Path dirPath, BasicFileAttributes attrs) throws IOException {
        if (recursiv || dir.equals(dirPath.toFile())) {
          return super.preVisitDirectory(dirPath, attrs);
        }
        return FileVisitResult.SKIP_SUBTREE;
      }
    });

    return result;
  }
}
