package de.duehl.basics.io;

/*
 * Copyright 2024 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.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Diese Klasse aktualisiert *.bat- und *.cmd-Dateien, die direkt im angegebenen Verzeichnis
 * liegen, und aktualisiert die Versionen von der übergebenen Jar darin auf gewünschte Weise.
 *
 * @version 1.03     2024-12-09
 * @author Christian Dühl
 */

public class BatchFileWithOwnJarActualiser {

    /** Das Verzeichnis in dem die Batchdateien modifiziert werden sollen. */
    private final String directory;

    /** Die Version des Projekts. */
    private final String version;

    /**
     * Das Pattern mit dem kompilierten regulären Ausdruck für den Namen der Jar-Datei bestehend
     * aus dem Namen des Projekts und einer beliebigen Version.
     */
    private final Pattern jarPattern;

    /**
     * Der neue Name der Jardatei bestehend aus dem Namen des Projekts und der aktuellen Version.
     */
    private final String newJarBareFilename;

    /** Die Liste aller gefundenen Batchdateien. */
    private List<String> batchFilenames;

    /** Die Liste der geänderten Batchdateien. */
    private List<String> actualisedFilenames;

    /**
     * Konstruktor.
     *
     * @param directory
     *            Das Verzeichnis in dem die Batchdateien modifiziert werden sollen.
     * @param name
     *            Der Name des Projekts.
     * @param version
     *            Die Version des Projekts.
     */
    public BatchFileWithOwnJarActualiser(String directory, String name, String version) {
        this.directory = directory;
        this.version = version;

        String jarRegex = "^.*(" + Pattern.quote(name) + "_[-_.0-9a-zA-Z]+\\.jar)\\b.*";
        jarPattern = Pattern.compile(jarRegex);

        newJarBareFilename = name + "_" + version + ".jar";
    }

    /**
     * In dieser Methode werden .bat- und .cmd-Dateien im Installationsverzeichnis
     * darauf untersucht, ob sie (ältere) Versionen der gerade erstellten Jar-Datei
     * nutzen. In diesem Fall werden diese Dateien auf die neue Version angepasst.       <br/><br/>
     *
     * Ist die Version leer (z.B. wenn man in Jarify ein Projekt ohne Version
     * installiert), werden KEINE Batchdateien angepasst.
     * Der Name bleibt dann ja auch gleich.
     *
     * @return Liste mit den geänderten Dateinamen (ohne Pfad).
     */
    public void actualize() {
        if (!version.isEmpty()) {
            findAllBatchfiles();
            findFilesToActualise();
            changeFilesToChange();
        }
    }

    /** Ermittelt alle Kandidaten, also ale Batchdateien. */
    private void findAllBatchfiles() {
        batchFilenames = FileHelper.findFilesInMainDirectoryNio2WithExtensions(directory, ".bat",
                ".cmd");
    }

    /** Ermittelt in den Kandidaten die Dateien, die geändert werden müssen. */
    private void findFilesToActualise() {
        actualisedFilenames = new ArrayList<>();

        for (String filename : batchFilenames) {
            if (isFileToActualise(filename)) {
                actualisedFilenames.add(filename);
            }
        }
    }

    private boolean isFileToActualise(String filename) {
        List<String> allLines = FileHelper.readFileToList(filename);

        for (String line: allLines) {
            Matcher matcher = jarPattern.matcher(line);
            if (matcher.find()) {
                return true;
            }
        }

        return false;
    }

    /** Ändert die zu ändernden Batchdateien. */
    private void changeFilesToChange() {
        for (String filename : actualisedFilenames) {
            changeFile(filename);
        }
    }

    /** Ändert eine zu bearbeitende Batchdatei. */
    private void changeFile(String filename) {
        List<String> lines = FileHelper.readFileToList(filename);
        List<String> newLines = changeLines(lines);
        FileHelper.writeLinesToFile(newLines, filename);
    }

    /**
     * Ändert die eingelesenen Zeilen einer zu bearbeitenden Batchdatei.
     *
     * Alle Vorkommen werden bearbeiten, auch wenn in einer Zeile mehrere vorkommen.
     *
     * Da in der Schleife auch der Name mit der aktuellen Version gefunden wird, muss man immer ab
     * dem Ende des letzten Fundorts suchen, sonst ergibt sich eine Endlosschleife.
     */
    private List<String> changeLines(List<String> lines) {
        List<String> newLines = new ArrayList<>();

        for (String line: lines) {
            Matcher matcher = jarPattern.matcher(line);
            int lastStart = -1;
            while (matcher.find()) {
                int start = matcher.start(1);
                int end = matcher.end(1);
                if (start > lastStart) {
                    lastStart = start;
                    String front = line.substring(0, start);
                    String rear = line.substring(end);
                    line = front + newJarBareFilename + rear;
                    matcher = jarPattern.matcher(line);
                }
            }
            newLines.add(line);
        }

        return newLines;
    }

    /** Getter für die Liste der geänderten Batchdateien. */
    public List<String> getActualisedFilenames() {
        return actualisedFilenames;
    }

}
