package de.duehl.basics.io.textfile;

/*
 * Copyright 2022 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 de.duehl.basics.io.Charset;
import de.duehl.basics.io.FileHelper;

/**
 * Diese Klasse liest eine mit Tabulatoren getrennten Textdatei in eine Liste von Überschriften-
 * Feldern und in eine Liste von Listen der Datenfelder pro Zeile aus ein.
 *
 * @version 1.01     2022-07-26
 * @author Christian Dühl
 */

public class HeaderAndDataFromTabSeparatedFileReader {

    /** Name der einzulesenden Textdatei. */
    private final String filename;

    /** Kodierung der Textdatei. */
    private final Charset charset;

    /** Die Überschriften aus der Textdatei. */
    private List<String> titles;

    /** Die Daten aus der Textdatei (ohne Überschriften). */
    private List<List<String>> listOfDataSets;

    /** Gibt an, ob die eingelesene Datei bis auf die Titelzeile leer sein darf. */
    private boolean fileMayContainOnlyHeaderLine;

    /**
     * Konstruktor.
     *
     * @param filename
     *            Name der einzulesenden mit Tabulatoren getrennten Textdatei.
     *            Hier wird die Kodierung ISO_8859_1 zum Einlesen verwende.
     */
    public HeaderAndDataFromTabSeparatedFileReader(String filename) {
        this(filename, Charset.ISO_8859_1);
    }

    /**
     * Konstruktor.
     *
     * @param filename
     *            Name der einzulesenden mit Tabulatoren getrennten Textdatei.
     * @param charset
     *            Kodierung der Textdatei.
     */
    public HeaderAndDataFromTabSeparatedFileReader(String filename, Charset charset) {
        this.filename = filename;
        this.charset = charset;

        fileMayContainOnlyHeaderLine = false;
        listOfDataSets = new ArrayList<>();
    }

    /** Lässt zu, dass die Dateien nur die Titelzeile enthalten. */
    public void allowFileMayContainOnlyHeaderLine() {
        fileMayContainOnlyHeaderLine = true;
    }

    /** Liest und überprüft die Datei. */
    public void readAndCheck() {
        List<List<String>> allFields = readFile(filename);

        if (allFields.isEmpty()) {
            throw new RuntimeException("Die Datei ist leer: " + filename);
        }

        titles = allFields.get(0);
        listOfDataSets.clear();
        for (int index = 1; index < allFields.size(); ++index) {
            listOfDataSets.add(allFields.get(index));
        }

        checkDataLinesNotEmpty();
    }

    private List<List<String>> readFile(String filename) {
        List<List<String>> fields = FileHelper.readFileToListOfFieldLines(filename, charset);

        if (fields.isEmpty()) {
            if (!fileMayContainOnlyHeaderLine) {
                throw new RuntimeException("Leere Datei\n\t" + filename);
            }
        }
        else {
            checkAllLinesHaveEqualNumberOfFields(fields, filename);
        }

        return fields;
    }

    private void checkAllLinesHaveEqualNumberOfFields(List<List<String>> fields, String filename) {
        int lengthOfTitles = fields.get(0).size();
        for (int index = 1; index < fields.size(); ++index) {
            int length =  fields.get(index).size();
            if (length != lengthOfTitles) {
                throw new RuntimeException("Abweichende Zeilenlänge in Zeile " + (index + 1)
                        + " in der Datei\n\t" + filename);
            }
        }
    }

    private void checkDataLinesNotEmpty() {
        if (listOfDataSets.isEmpty() && !fileMayContainOnlyHeaderLine) {
            throw new RuntimeException("Die Datei\n\t" + filename + "\n"
                    + "enthält keine Daten!");
        }
    }

    /** Getter für den Namen der einzulesenden Textdatei. */
    protected String getFilename() {
        return filename;
    }

    /** Getter für die Überschriften aus der Eingabedatei. */
    public List<String> getTitles() {
        return titles;
    }

    /** Getter für die Daten aus der Eingabedatei (ohne Überschriften). */
    public List<List<String>> getListOfDataSets() {
        return listOfDataSets;
    }

    /** Gibt die Anzahl der Zeilen mit Daten aus der Eingabedatei (ohne Überschriften) zurück. */
    public int getSizeOfDataLines() {
        return listOfDataSets.size();
    }

}
