package de.duehl.basics.system.windows;

/*
 * 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.io.File;
import java.io.FileWriter;
import java.io.IOException;

/**
 * Diese Klasse erstellt Verknüpfungen unter Windows (*.lnk-Dateien) unter der Verwendung von
 * Visual-Basic.                                                                            <br><br>
 *
 * Basierend auf dem Thread                                                   <br>&nbsp;&nbsp;&nbsp;
 *     https://stackoverflow.com/questions/13145942/creating-a-shortcut-file-from-java          <br>
 * und dem Code von Jackson N. Brienen auf der Seite                          <br>&nbsp;&nbsp;&nbsp;
 *     https://github.com/JacksonBrienen/VBS-Shortcut/blob/main/src/com/github/jbrienen/vbs_sc/
 *         ShortcutFactory.java                                                             <br><br>
 *
 * Siehe auch: https://www.vbsedit.com/html/e6ac108b-15f6-4a54-891f-589e8b687ace.asp        <br><br>
 *
 * Man ruft mindestens eine der beiden Methoden                               <br>&nbsp;&nbsp;&nbsp;
 *     createDesktopShortcut()                                                                 <br>
 * oder                                                                       <br>&nbsp;&nbsp;&nbsp;
 *    createShortcutAtGivenPath()                                                              <br>
 * auf sowie eine beliebige Auswahl der folgenden Methoden:                   <br>&nbsp;&nbsp;&nbsp;
 *    setIcon()                                                                                <br>
 *    setWorkingDirectory()                                                                    <br>
 * Am Ende ruft man die Methode                                          <br>&nbsp;&nbsp;&nbsp;
 *     create()
 * auf.
 *
 * @version 1.01     2024-12-08
 * @author Christian Dühl
 */

public class WindowsShortcutCreator {

    /** Der Dateiname der Quelle, zu der die Verknüpfung angelegt werden soll, mit Pfad. */
    private final File sourceFile;

    /** Der Dateiname der anzulegenden Verknüpfung mit Pfad. */
    private String linkFilename;

    /**
     * Der Name des Image/Icon, das für die Verknüpfung verwendet wird, mit Pfad.
     *
     * Ist er leer, wird kein Icon hinzugefügt.
     */
    private String iconFilename;

    /**
     * Das Arbeitsverzeichnis, in dem das Ziel der Verknüpfung ausgeführt werden soll.
     *
     * Ist er leer, wird kein Icon hinzugefügt.
     */
    private String workingDirectory;

    /**
     * Konstruktor.
     *
     * @param sourceFilename
     *            Der Dateiname der Quelle, zu der die Verknüpfung angelegt werden soll, mit Pfad.
     */
    public WindowsShortcutCreator(String sourceFilename) {
        sourceFile = new File(sourceFilename);
        iconFilename = "";
        workingDirectory = "";
    }

    /**
     * Legt fest, dass die Verknüpfung auf dem Desktop angelegt werden soll.
     *
     * @param linkName
     *            Der Name der Verknüpfung auf dem Desktop, dieser darf keinen Pfad enthalten.
     */
    public void createDesktopShortcut(String linkName) {
        createShortcutAtGivenPath(createDesktopLinkPath(linkName));
    }

    private static String createDesktopLinkPath(String linkName) {
        return System.getProperty("user.home") + "\\Desktop\\" + linkName;
    }

    /**
     * Legt fest, dass die Verknüpfung an der gegebenen Stelle angelegt werden soll
     *
     * @param linkPath
     *            Der Dateiname der anzulegenden Verknüpfung mit Pfad.
     */
    public void createShortcutAtGivenPath(String linkPath) {
        this.linkFilename = linkPath;
    }

    /** Legt das anzuzeigende Icon fest. */
    public void setIcon(String iconFilename) {
        this.iconFilename = iconFilename;
    }

    /** Legt das Arbeitsverzeichnis fest. */
    public void setWorkingDirectory(String workingDirectory) {
        this.workingDirectory = workingDirectory;
    }

    /** Erzeugt die Verknüpfung. */
    public void create() {
        checkSourceFileExists();
        checkLinkFilenameEndsWithLnk();


        StringBuilder builder = new StringBuilder();
        builder.append("Set wsObj = WScript.CreateObject(\"WScript.shell\")\n");
        builder.append("scPath = \"").append(linkFilename).append("\"\n");
        builder.append("Set scObj = wsObj.CreateShortcut(scPath)\n");

        String absoluteSource = sourceFile.getAbsolutePath();
        builder.append("\tscObj.TargetPath = \"").append(absoluteSource).append("\"\n");

        if (!iconFilename.isBlank()) {
            File iconFile = new File(iconFilename);
            if (!iconFile.exists()) {
                throw new RuntimeException(
                        "Der Pfad " + iconFile.getAbsolutePath() + " existiert nicht.");
            }
            String absoluteIconPath = iconFile.getAbsolutePath();
            builder.append("\tscObj.IconLocation = \"").append(absoluteIconPath).append("\"\n");
        }

        if (!workingDirectory.isBlank()) {
            File workingDirectoryFile = new File(workingDirectory);
            if (!workingDirectoryFile.isDirectory()) {
                throw new RuntimeException("Das Verzeichnis "
                        + workingDirectoryFile.getAbsolutePath() + " existiert nicht.");
            }
            String absoluteWorkingDirectoryPath = workingDirectoryFile.getAbsolutePath();
            builder.append("\tscObj.WorkingDirectory = \"").append(absoluteWorkingDirectoryPath)
                    .append("\"\n");
        }
        builder.append("scObj.Save\n");
        String vbsCode = builder.toString();

        createRunAndDeleteVbs(vbsCode);
    }

    private void checkSourceFileExists() {
        if (!sourceFile.exists()) {
            throw new RuntimeException(
                    "Der Pfad " + sourceFile.getAbsolutePath() + " existiert nicht.");
        }
    }

    private void checkLinkFilenameEndsWithLnk() {
        if (!linkFilename.endsWith(".lnk")) {
            throw new RuntimeException("Der Dateiname der Verknüppfung muss auf '.lnk' enden.");
        }
    }

    /**
     * Erzeugt ein VBS-Skript, mit dem Code, führt ihn aus und löscht die Datei anschließend
     * wieder.
     */
    private void createRunAndDeleteVbs(String vbsCode) {
        try {
            tryToCreateRunAndDeleteVbs(vbsCode);
        }
        catch (Exception exception) {
            throw new RuntimeException("Beim Versuch VBS-Code in eine temporäre Datei zu "
                    + "schreiben und auszuführen, trat ein Problem auf.\n"
                    + "\t" + "vbsCode: \n"
                    + vbsCode + "\n"
                    + "\t" + "Meldung: " + exception.getMessage() + "\n"
                    + "Möglicherweise ist die temporär angelegte Datei mit dem VBS-Code noch "
                    + "vorhanden.\n"
                    );
        }
    }

    private void tryToCreateRunAndDeleteVbs(String vbsCode)
            throws IOException, InterruptedException {
        File script = File.createTempFile("verknuepfung_anlegen_", ".vbs");

        FileWriter writer = new FileWriter(script);
        writer.write(vbsCode);
        writer.close();

        Process process = Runtime.getRuntime().exec("wscript \"" + script.getAbsolutePath() + "\"");
        process.waitFor(); // waits for process to finish

        boolean deletionSuccess = script.delete();
        if (!deletionSuccess) {
            System.err.println("Warnung: Die temporär angelegte Datei '" + script.getAbsolutePath()
                    + "' konnte nicht gelöscht werden.");
        }
    }

}