import java.io.IOException;
import java.nio.file.DirectoryIteratorException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class BlackBox {
  private static final String EXECUTABLES_DIRECTORY = "executables";
  private static final String EXECUTABLE_NAME_PREFIX = "check";
  private static final String EXECUTABLE_NAME_SUFFIX = "exe";
  private static final String IMAGES_DIRECTORY = "images";
  public static final String IMAGES_FILE_SUFFIXES = "ppm,pgm,pbm";
  public static final String IMAGES_FILE_PREFIX = "test";
  private final String executablesDirectoryPath;
  private final String imagesDirectoryPath;

  public static final String ANSI_RED = "\u001B[31m";
  public static final String ANSI_GREEN = "\u001B[32m";
  public static final String ANSI_RESET = "\u001B[0m";

  public BlackBox(String executablesDirectoryPath, String imagesDirectoryPath){
    this.executablesDirectoryPath = executablesDirectoryPath;
    this.imagesDirectoryPath = imagesDirectoryPath;
  }

  private List<Path> getImagePaths() throws IOException {
    return getDirectoryFiles(imagesDirectoryPath, IMAGES_FILE_PREFIX + "*.{" + IMAGES_FILE_SUFFIXES + "}");
  }

  private List<Path> getExecutablePaths() throws IOException {
    return getDirectoryFiles(executablesDirectoryPath, EXECUTABLE_NAME_PREFIX + "*" + EXECUTABLE_NAME_SUFFIX);
  }
  private List<Path> getDirectoryFiles(String directoryPath, String glob) throws IOException {
    List<Path> result = new ArrayList<>();
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(Path.of(directoryPath), glob)) {
      for (Path entry: stream) {
        result.add(entry);
      }
    } catch (DirectoryIteratorException ex) {
      throw ex.getCause();
    }
    Collections.sort(result);
    return result;
  }

  private boolean runTest(String executablePath, String... args) {
    List<String> command = new ArrayList<>();
    command.add(executablePath);
    command.addAll(List.of(args));
    ProcessBuilder processBuilder = new ProcessBuilder(command);
    try {
      Process process = processBuilder.start();
      return process.waitFor() == 0;
    }
    catch (Exception exception){
      exception.printStackTrace();
    }
    return false;
  }

  public void runTestSuites() throws IOException{
    for(Path executablePath : getExecutablePaths()) {
      runTestSuite(executablePath);
    }
  }

  private void printTestResult(String text, boolean isSuccessful){
    String color = isSuccessful ? ANSI_GREEN : ANSI_RED;
    System.out.println(color + text + ANSI_RESET);
  }

  private void runTestSuite(Path executablePath) throws IOException {
    String executableName = executablePath.getFileName().toString();
    System.out.println("-".repeat(70));
    System.out.println("Test suite for " + executableName);
    boolean testSuiteIsSuccessful = true;
    for (Path imagePath : getImagePaths()) {
      String imageName = imagePath.getFileName().toString();
      boolean expected_result = imageName.contains("_Y");
      boolean actual_result = runTest(executablePath.toString(), imagePath.toString());
      printTestResult(executableName + " " + imageName + ", expected: "
              + expected_result + ",\tactual: " + actual_result, actual_result==expected_result);
      if (actual_result != expected_result) {
        testSuiteIsSuccessful = false;
      }
    }
    printTestResult("Test suite for " + executableName + " successful: " + testSuiteIsSuccessful,
            testSuiteIsSuccessful);
  }

  public static void main(String[] args) throws IOException{
    new BlackBox(EXECUTABLES_DIRECTORY, IMAGES_DIRECTORY).runTestSuites();
  }
}