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.io.FileHelper;
import de.duehl.basics.text.Text;
import de.duehl.basics.text.xml.XmlAnalyser;

/**
 * Diese Klasse stellt Methoden zum Speichern und Laden sehr einfacher, eigener XML-Dateien bereit,
 * hier werden im Unterschied zu OwnXmlIO statt XML-Zeilen Objekte gesammelt, welche XmlStorable
 * implementieren, dadurch benötigt man hier keinen xmlSingleIdentifier.
 *
 * Sie sollte nicht für XML-Dateien aus anderen Quellen benutzt werden!
 *
 * @version 1.01     2021-11-10
 * @author Christian Dühl
 */

public class XmlStorableIO {

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

    /** Liste zu speichernder Daten-Objekte. */
    private final List<XmlStorable> storables;

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

    /** Fügt ein zu speicherndes Daten-Objekt hinzu. */
    public void addStorable(XmlStorable storable) {
        storables.add(storable);
    }

    /**
     * Speichert die Liste zu speichernder Daten-Objekte in einer Datei, z.B. für die Persistenz.
     */
    public void save(String filename) {
        FileHelper.writeLinesToFile(createSaveLines(), filename);
    }

    /**
     * Erzeugt aus der Liste zu speichernder Daten-Objekte 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> lines = new ArrayList<>();

        lines.add("<" + xmlCollectionTag + ">");
        for (XmlStorable storable : storables) {
            String indentedXmlText = Text.indent(storable.toXml());
            List<String> indentedXmlLines = Text.splitByLineBreaks(indentedXmlText);
            lines.addAll(indentedXmlLines);
        }
        lines.add("</" + xmlCollectionTag + ">");

        return lines;
    }

    /**
     * Diese Methode liest aus den übergebenen Zeilen einer Xml-Datei alle
     * gleichförmigen Daten-Objekte aus.                                                   <br><br>
     *
     * Achtung:                                                                                <br>
     * 1) Dies ist nicht exakt das Gegenstück zur hiesigen save()-Methode!                     <br>
     * 2) Die ggf. in dieser Klasse gespeicherten XmlStorables spielen hier auch keine
     *    Rolle, diese sind nur zum Speichern da.
     *
     * @param xmlLines
     *            Zeilen aus einer XML-Datei.
     * @param xmlDataTag
     *            Tag der einzelnen XML-Elemente (etwa 'sector' für <sector> und </sector>)
     * @param creator
     *            Objekt das aus einer eingelesenen Zeile ein wirkliches Daten-Objekt erzeugen und
     *            dieses speichern kann.
     */
    public void loadFromXmlLines(List<String> xmlLines, String xmlDataTag,
            SingleElementFromLineCreator creator) {
        List<String> xmlLinesWithOneLinerPerData = createListOfConnectedLines(xmlLines,
                xmlCollectionTag, xmlDataTag);

        List<String> xmlLinesWithOneLinerPerDataWithFrame = new ArrayList<>();
        xmlLinesWithOneLinerPerDataWithFrame.add("<" + xmlCollectionTag + ">");
        xmlLinesWithOneLinerPerDataWithFrame.addAll(xmlLinesWithOneLinerPerData);
        xmlLinesWithOneLinerPerDataWithFrame.add("</" + xmlCollectionTag + ">");

        OwnXmlIO xmlIo = new OwnXmlIO(xmlCollectionTag, xmlDataTag);
        xmlIo.loadFromXmlLines(xmlLinesWithOneLinerPerDataWithFrame, creator);
    }

    /**
     * Erzeugt aus den Zeilen einer XML-Datei so aufbereitete "Zeilen" (zu einem String
     * zusammengesetzte Zeilen), dass diese passend weiterverarbeitet werden können.
     *
     * @param xmlLines
     *            Zeilen aus einer XML-Datei.
     * @param collectionTag
     *            Tag der Sammlung von XML-Elementen (etwa 'sectors' für <sectors> und </sectors>)
     * @param xmlDataTag
     *            Tag der einzelnen XML-Elemente (etwa 'sector' für <sector> und </sector>)
     */
    public static List<String> createListOfConnectedLines(List<String> xmlLines,
            String collectionTag, String xmlDataTag) {
        List<String> xmlLinesWithMultipleDataObjects =
                XmlAnalyser.getLinesInTag(xmlLines, collectionTag);
        List<List<String>> listOfDataXmlLines = XmlAnalyser.getMultipleLinesInTagAllowEmptyList(
                xmlLinesWithMultipleDataObjects, xmlDataTag);

        List<String> xmlLinesWithOneLinerPerData = new ArrayList<>();
        for (List<String> dataXmlLines : listOfDataXmlLines) {
            xmlLinesWithOneLinerPerData.add(Text.joinWithLineBreak(dataXmlLines));
        }

        return xmlLinesWithOneLinerPerData;
    }

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

}
