package de.duehl.basics.test;

/*
 * Copyright 2019 Christian Dühl. All rights reserved.
 *
 * This program is free software. You can redistribute it and/or
 * modify it under the same terms as perl:
 *
 * general:  http://dev.perl.org/licenses/
 * GPL:      http://dev.perl.org/licenses/gpl1.html
 * artistic: http://dev.perl.org/licenses/artistic.html
 */

import java.io.File;
import java.net.URL;
import java.util.List;

import de.duehl.basics.collections.CollectionsHelper;
import de.duehl.basics.io.FileHelper;

/**
 * Diese Klasse stellt Hilfsmethoden für Tests mit lokal im Projekt unter
 * bestimmten Pfaden wie etwa .../test/data/ abgelegten Testdaten dar.                      <br><br>
 *
 * Ersetzt ein Konstrukt wie                                                                   <pre>
 *
 *     private String path() {
 *         String className = getClass().getName();
 *         int dotIndex = className.indexOf(".");
 *         while (dotIndex > -1) {
 *             className = className.substring(dotIndex + 1); // endet nicht auf .
 *             dotIndex = className.indexOf(".");
 *         }
 *
 *         URL url = getClass().getResource(className + ".class");
 *         File f = new File(url.getPath());
 *         f = new File(f.getParent());
 *         f = new File(f.getParent());
 *         f = new File(f.getParent());
 *         f = new File(f.getParent());
 *         f = new File(f.getParent());
 *         f = new File(f.getParent());
 *         String p = f.getPath() + File.separator + "data" + File.separator
 *                 + "testfiles" + File.separator;
 *         return p;
 *     }                                                                                      </pre>
 *
 * @version 1.01     2019-11-28
 * @author Christian Dühl
 */

public class TestHelper {

    /** Pfad zu den Testdaten (.../ProjectName/test/data/). */
    private final String testDataPath;

    /** Testklasse. */
    private final Class<?> testClass;

    /** Anzahl der Verzeichnisebenen des aufrufenden Tests über dem Projektverzeichnis. */
    private final int levelsAboveProjectDirectory;

    /**
     * Name der Testklasse (der letzter Teil des übergebenen Klassennamens nach dem letzten Punkt).
     */
    private final String className;

    /** Das Basisverzeichnis des Projektes. */
    private final String projectDirectory;

    /**
     * Namen der Unterverzeichnisse, die vom Projektverzeichnis zum gewünschten Testverzeichnis
     * führen, vermutlich beginnend mit "test".
     */
    private final List<String> subDirectoriesFromProjectBaseToTestfiles;

    /**
     * Konstruktor für ein frei wählbares Testverzeichnis lokal zum Projektverzeichnis.
     *
     * @param testClass
     *            Testklasse.
     * @param levelsAboveProjectDirectory
     *            Anzahl der Verzeichnisebenen des aufrufenden Tests über dem Projektverzeichnis.
     * @param subDirectoriesFromProjectBaseToTestfiles
     *            Namen der Unterverzeichnisse, die vom Projektverzeichnis zum gewünschten
     *            Testverzeichnis führen, vermutlich beginnend mit "test".
     */
    public TestHelper(Class<?> testClass, int levelsAboveProjectDirectory,
            List<String> subDirectoriesFromProjectBaseToTestfiles) {
        this.testClass = testClass;
        this.levelsAboveProjectDirectory = levelsAboveProjectDirectory;
        this.subDirectoriesFromProjectBaseToTestfiles = subDirectoriesFromProjectBaseToTestfiles;
        className = determinePureClassName();
        projectDirectory = determineProjectDirectory();
        testDataPath = determineTestDataPath();
    }

    private String determinePureClassName() {
        String className = testClass.getName();
        int dotIndex = className.lastIndexOf(".");
        return className.substring(dotIndex + 1); // endet nicht auf .
    }

    private String determineProjectDirectory() {
        URL url = testClass.getResource(className + ".class");
        File file = new File(url.getPath());
        for (int levelIndex = 0; levelIndex < levelsAboveProjectDirectory; ++levelIndex) {
            file = new File(file.getParent());
        }
        return file.getPath();
    }

    private String determineTestDataPath() {
        //System.out.println("projectDirectory: " + projectDirectory);

        String testDataPath = projectDirectory;
        for (String subDirName : subDirectoriesFromProjectBaseToTestfiles) {
            testDataPath = FileHelper.concatPathes(testDataPath, subDirName);

        }
        //System.out.println("testDataPath: " + testDataPath);

        return testDataPath;
    }

    /**
     * Getter für den Namen der Testklasse (der letzter Teil des übergebenen Klassennamens nach dem
     * letzten Punkt).
     */
    public String getClassName() {
        return className;
    }

    /** Getter für das Basisverzeichnis des Projektes. */
    public String getProjectDirectory() {
        return projectDirectory;
    }

    /** Getter für den Pfad zu den Testdaten (.../ProjectName/test/data/ oder frei gewählt). */
    public String getTestDataPath() {
        return testDataPath;
    }

    /**
     * Getter für einen Dateinamen im Hauptverzeichnis der Testdaten (.../ProjectName/test/data/
     * oder frei gewählt).
     *
     * @param filename
     *            Name der Datei ohne Pfad.
     * @return Name der Datei mit Pfad (.../ProjectName/test/data/filename).
     */
    public String getTestFile(String filename) {
        return FileHelper.concatPathes(testDataPath, filename);
    }

    /**
     * Getter für ein Verzeichnis im Hauptverzeichnis der Testdaten (.../ProjectName/test/data/
     * oder frei gewählt).
     *
     * @param dirname
     *            Name des Verzeichnisses ohne Pfad.
     * @return Name des Verzeichnisses mit Pfad (.../ProjectName/test/data/dirname).
     */
    public String getTestDir(String dirname) {
        return getTestFile(dirname);
    }

    /**
     * Getter für einen Dateinamen in einem Unterverzeichnis des Hauptverzeichnis der Testdaten
     * (.../ProjectName/test/data/pureSubDirName/ oder frei gewählt/pureSubDirName/).
     *
     * @param pureSubDirName
     *            Name eines zwischengelagerten Verzeichnisses ohne Pfad.
     * @param filename
     *            Name der Datei ohne Pfad.
     * @return Name der Datei mit Pfad (.../ProjectName/test/data/pureSubDirName/filename).
     */
    public String getTestFileInSubDir(String pureSubDirName, String filename) {
        return FileHelper.concatPathesMultiple(testDataPath, pureSubDirName, filename);
    }

    /**
     * Erzeugt ein Objekt mit den passenden Pfaden für hup.extended.regular.expression.system und
     * hup.impressum für das Testverzeichnis .../test/data/.
     *
     * @param testClass
     *            Testklasse.
     * @param levelsAboveProjectDirectory
     *            Anzahl der Verzeichnisebenen des aufrufenden Tests über dem Projektverzeichnis.
     */
    public static TestHelper createForTestData(Class<?> testClass,
            int levelsAboveProjectDirectory) {
        return new TestHelper(testClass, levelsAboveProjectDirectory,
            CollectionsHelper.buildListFrom("test", "data"));
    }

    /**
     * Erzeugt ein Objekt mit den passenden Pfaden für de.duehl.basics für das Testverzeichnis
     * .../data/testfiles/.
     *
     * @param testClass
     *            Testklasse.
     * @param levelsAboveProjectDirectory
     *            Anzahl der Verzeichnisebenen des aufrufenden Tests über dem Projektverzeichnis.
     */
    public static TestHelper createForBasics(Class<?> testClass, int levelsAboveProjectDirectory) {
        return new TestHelper(testClass, levelsAboveProjectDirectory,
            CollectionsHelper.buildListFrom("data", "testfiles"));
    }

}
