package de.duehl.basics.text.xml.own;

/*
 * 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.util.ArrayList;
import java.util.List;

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

/**
 * Diese Klasse stellt Methoden zum Speichern und Laden sehr einfacher, eigener XML-Dateien bereit.
 *
 * Sie sollte nicht für XML-Dateien aus anderen Quellen benutzt werden!
 *
 * @version 1.01     2021-11-10
 * @author Christian Dühl
 */

public class OwnXmlIO {

    /**
     * Tag des öffnenden und schließenden XML-Elements um die einzelnen XML-Elemente (etwa
     * 'sectors' für <sectors> und </sectors>).
     */
    private final String xmlCollectionTag;

    /** Tag der einzelnen XML-Elemente (etwa 'sector' für <sector> und </sector>). */
    private final String xmlDataTag;

    /**
     * Konstruktor.
     *
     * @param xmlCollectionTag
     *            Tag des öffnenden und schließenden XML-Elements um die einzelnen XML-Elemente
     *            (etwa 'sectors' für <sectors> und </sectors>).
     * @param xmlDataTag
     *            Tag der einzelnen XML-Elemente (etwa 'sector' für <sector> und </sector>).
     */
    public OwnXmlIO(String xmlCollectionTag, String xmlDataTag) {
        this.xmlCollectionTag = xmlCollectionTag;
        this.xmlDataTag = xmlDataTag;
    }

    /**
     * Diese Methode liest aus den übergebenen Zeilen einer Xml-Datei alle gleichförmigen
     * Daten-Objekte aus.
     *
     * @param xmlLines
     *            Zeilen aus einer XML-Datei.
     * @param creator
     *            Objekt das aus einer eingelesenen Zeile ein wirkliches Daten-Objekt erzeugen und
     *            dieses speichern kann.
     */
    public void loadFromXmlLines(List<String> xmlLines, SingleElementFromLineCreator creator) {
        boolean inElement = false;
        for (String line : xmlLines) {
            line = line.trim();
            if (line.equals("<" + xmlCollectionTag + ">")) {
                inElement = true;
                continue;
            }
            if (line.equals("</" + xmlCollectionTag + ">")) {
                inElement = false;
                continue;
            }
            if (inElement) {
                if (line.startsWith("<" + xmlDataTag + ">")
                        && line.endsWith("</" + xmlDataTag + ">")) {
                    creator.createAndStoreElementFromLine(line);
                }
                else {
                    throw new IllegalArgumentException("Kein <" + xmlDataTag + ">...</"
                            + xmlDataTag + "> in der Zeile gefunden.\n"
                            + "Zeile ist (getrimmt):\n\t" + line + "\n"
                            + "Gesamte Xml-Zeilen sind:\n"
                            + CollectionsHelper.listListNice(xmlLines));
                }
            }
        }
    }

    /**
     * Lädt die übergebene XML-Datei und verarbeitet die gefundenen Daten der einzelnen Elemente.
     *
     * @param filename
     *            Name der XML-Datei mit den Elementen.
     * @param creator
     *            Objekt das aus einer eingelesenen Zeile ein wirkliches Daten-Objekt erzeugen und
     *            dieses speichern kann.
     */
    public void load(String filename, SingleElementFromLineCreator creator) {
        List<String> xmlLines = FileHelper.readFileToList(filename);
        loadFromXmlLines(xmlLines, creator);
    }

    /**
     * Lädt die übergebene XML-Datei und verarbeitet die gefundenen Daten der einzelnen Elemente.
     *
     * @param filename
     *            Name der XML-Datei mit den Elementen.
     * @param creator
     *            Objekt das aus einer eingelesenen Zeile ein wirkliches Daten-Objekt erzeugen und
     *            dieses speichern kann.
     */
    public void loadFromVeryLongFile(String filename, SingleElementFromLineCreator creator) {
        FineFileReader reader = new FineFileReader(filename);
        String line;
        boolean inElement = false;
        while ((line = reader.readNextLine()) != null) {
            line = line.trim();
            if (line.equals("<" + xmlCollectionTag + ">")) {
                inElement = true;
                continue;
            }
            if (line.equals("</" + xmlCollectionTag + ">")) {
                inElement = false;
                continue;
            }
            if (inElement) {
                if (line.startsWith("<" + xmlDataTag + ">")
                        && line.endsWith("</" + xmlDataTag + ">")) {
                    creator.createAndStoreElementFromLine(line);
                }
                else {
                    throw new IllegalArgumentException("Kein <" + xmlDataTag + ">...</"
                            + xmlDataTag + "> in Zeile " + reader.getLineNumber()
                            + " gefunden. Zeile ist (getrimmt):\n\t" + line + "\n"
                            + "Datei ist: " + filename + "\n");
                }
            }
        }
        reader.close();
    }

    /**
     * Speichert die Liste von Zeilen von XML-Datensätzen (hier wird erwartet, dass diese in
     * xmlDataTag gehüllt sind) in einer Datei, z.B. für die Persistenz.
     */
    public void save(String filename, List<String> singleElementXmlLines) {
        FileHelper.writeLinesToFile(createSaveLines(singleElementXmlLines), filename);
    }

    /**
     * Erzeugt aus einer Liste von Zeilen von XML-Datensätzen (hier wird erwartet, dass diese in
     * xmlDataTag gehüllt sind) eine Liste von XML-Zeilen, wie man sie in einer Datei
     * speichern könnte, z.B. für die Persistenz.
     */
    public List<String> createSaveLines(List<String> singleElementXmlLines) {
        List<String> lines = new ArrayList<>();

        lines.add("<" + xmlCollectionTag + ">");
        for (String singleElement : singleElementXmlLines) {
            lines.add("    " + singleElement);
        }
        lines.add("</" + xmlCollectionTag + ">");

        return lines;
    }

}
