package de.duehl.basics.system.starter;

/*
 * Copyright 2021 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.IOException;

import de.duehl.basics.io.FineFileReader;
import de.duehl.basics.io.FineFileWriter;
import de.duehl.basics.logging.Logger;
import de.duehl.basics.logging.NoLogger;
import de.duehl.basics.logic.ErrorHandler;

/**
 * Diese Klasse ruft SAS mit einem Programm auf und wartet auf die Ausführung.
 * Sollte also nur aus einem Worker-Thread gestartet werden!
 *
 * @version 1.01     2021-05-27
 * @author Christian Dühl
 */

public class SasStarter extends Starter {

    /** Gibt die Geschmacksrichtung des zu startenden SAS an. */
    public enum SasType {
        /** mit der MPO "ohne" Konfiguration */
        BANZ_MPO_OHNE,

        /** mit der MPO "mit" Konfiguration (MPO-Standard) */
        BANZ_MPO,

        /** Workverzeichnis in nicht gesichertem Bereich schreiben. */
        AS400,

        /** Start ohne besondere Konfigurationsdatei. */
        NORMAL
    }

    /** SAS-Binary (mit Pfad). */
    private String sasBinary;

    /**
     * Konstruktor.
     *
     * @param sasBinary
     *            SAS-Binary (mit Pfad).
     * @param error
     *            Objekt, das die Fehlerbehandlung durchführt.
     */
    public SasStarter(String sasBinary, ErrorHandler error) {
        this(sasBinary, new NoLogger(), error);
    }

    /**
     * Konstruktor.
     *
     * @param sasBinary
     *            SAS-Binary (mit Pfad).
     * @param logger
     *            Der Logger für den Ablauf dieser Session.
     * @param error
     *            Objekt, das die Fehlerbehandlung durchführt.
     */
    public SasStarter(String sasBinary, Logger logger, ErrorHandler error) {
        super(logger, error);
        this.sasBinary = sasBinary;
    }

    /**
     * Führt Sas aus und wartet, bis es fertig ist. Da hier kein Skript
     * mitgegeben wird, wird hoffentlich das Programm geöffnet. Sas Optionen
     * vgl. http://v8doc.sas.com/sashtml/unixc/syopts.htm
     *
     * @param type
     *            Typ des Sas-Programms.
     * @param program
     *            Auszuführendes SAS-Programm.
     */
    public String runInteractive(SasType type, String program) {
        log("Start, type = " + type + ", program = " + program);

        final String[] params;
        if (type != SasType.NORMAL) {
            String[] p = {
                    sasBinary,
                    //"-ALTLOG", log, TODO: Mal probieren!
            };
            params = p;
        }
        else {
            String[] p = {
                    sasBinary,
                    "-CONFIG", config(type),
                    //"-ALTLOG", log, TODO dito
            };
            params = p;
        }
        ProcessBuilder processBuilder = new ProcessBuilder(params);
        Process process = null;
        try {
            process = processBuilder.start();
        }
        catch (IOException e) {
            //e.printStackTrace();
            error.error("Es trat ein Fehler bei der Ausführung des SAS"
                    + "-Prozesses auf.");
        }
        try {
            process.waitFor();
        }
        catch (InterruptedException e) {
            // nichts, das kann nicht unterbrochen werden.
        }

        /* Aufgerufenen Befehl als String zurückliefern: */
        log("Ende, type = " + type + ", program = " + program);
        return paramsToString(params);
    }

    /**
     * Führt Sas aus und wartet, bis es fertig ist.
     * Sas Optionen vgl. http://v8doc.sas.com/sashtml/unixc/syopts.htm
     *
     * @param type
     *            Typ des Sas-Programms.
     * @param program
     *            Auszuführendes SAS-Programm.
     * @param log
     *            Logfile, das erzeugt wird.
     * @param errorLog
     *            Extrakt der Fehler aus dem Logfile, wird ebenfalls erzeugt.
     */
    public String runAndWait(SasType type, String program, String log,
            String errorLog) {
        log("Start, type = " + type + ", program = " + program
                + ", log = " + log + ", errorLog = " + errorLog);

        final String[] params;
        if (type == SasType.NORMAL) {
            String[] p = {
                    sasBinary,
                    "-SYSIN", program,
                    "-LOG", log,
            };
            params = p;
        }
        else {
            String[] p = {
                    sasBinary,
                    "-CONFIG", config(type),
                    "-SYSIN", program,
                    "-LOG", log,
            };
            params = p;
        }
        ProcessBuilder processBuilder = new ProcessBuilder(params);
        Process process = null;
        try {
            process = processBuilder.start();
        }
        catch (IOException e) {
            //e.printStackTrace();
            error.error("Es trat ein Fehler bei der Ausführung des SAS"
                    + "-Prozesses auf.");
        }

        /* Auf Beendigung warten. */
        try {
            process.waitFor();
        }
        catch (InterruptedException e) {
            // nichts, das kann nicht unterbrochen werden.
        }

        /* Fehler raussuchen: */
        grepErrors(log, errorLog);

        /* Aufgerufenen Befehl als String zurückliefern: */
        log("Ende, type = " + type + ", program = " + program
                + ", log = " + log + ", errorLog = " + errorLog);
        return paramsToString(params);
    }

    /**
     * Ermittelt die zum Typ passende Konfigurationsdatei.
     *
     * Auf dem Srv6 ist in die Links eingetragen:
     * Normal: \\cluster1\Projekte1\HRTag\HR_Strukt_HuP\sasv9ohne.cfg
     * Banz:   \\cluster1\Projekte1\HRTag\HR_Strukt_HuP\sasv9.cfg
     * PS400:  C:\SAS\SAS 9.1\nls\en\SASV9P400.CFG
     *
     * @param type
     *            Typ des Sas-Programms.
     * @return Passende Konfigurationsdatei.
     */
    private String config(SasType type) {
        String config = null;
        switch (type) {
            case BANZ_MPO_OHNE:
                config = "//cluster1/Projekte1/HRTag/HR_Strukt_HuP/sasv9ohne.cfg";
                break;
            case BANZ_MPO:
                config = "//cluster1/Projekte1/HRTag/HR_Strukt_HuP/sasv9.cfg";
                break;
            case AS400:
                config = "C:/SAS/SAS 9.1/nls/en/SASV9P400.CFG"; // nur auf SRV6 !
                break;
            default:
                break;

        }
        return config;
    }

    /**
     * Ermittelt aus der Logdatei alle Fehler.
     *
     * @param log
     *            Logfile, das erzeugt wird.
     * @param errorLog
     *            Extrakt der Fehler aus dem Logfile, wird ebenfalls erzeugt.
     */
    private void grepErrors(String log, String errorLog) {
        FineFileReader reader = new FineFileReader(log);
        FineFileWriter writer = new FineFileWriter(errorLog);
        String line = reader.readNextLine();
        while (line != null) {
            String lowerLine = line.replace("_ERROR_", "_XXX_").toLowerCase();
            // Damit überlesen wir die _ERROR_ Variable, die öfter vorkommt.
            if (lowerLine.contains("error")) {
                writer.write(reader.getLineNumber() + ": ");
                writer.writeln(line);
            }
            line = reader.readNextLine();
        }
        writer.close();
        reader.close();
    }

}
