package de.duehl.basics.text;

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

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

/**
 * Diese Klasse stellt eine Menge von Zeilen dar, die zum Beispiel aus einer Datei eingelesen wurden.
 *
 * @version 1.01     2022-06-24
 * @author Christian Dühl
 */

public class Lines implements Iterable<String> {

    /** Liste der Zeilen. */
    private final List<String> lines;

    /** Konstruktor mit leerer Menge an Zeilen. */
    public Lines() {
        lines = new ArrayList<>();
    }

    /**
     * Konstruktor der die übergebene Liste von Zeilen übernimmt.
     *
     * @param lines
     *            Eine Liste mit Zeilen.
     */
    public Lines(List<String> lines) {
        this();
        this.lines.addAll(lines);
    }

    /**
     * Konstruktor der das übergebene Objekt von Zeilen übernimmt.
     *
     * @param lines
     *            Objekt mit Zeilen.
     */
    public Lines(Lines lines) {
        this(lines.getLines());
    }

    /** Fügt die übergebene Zeile zur Liste der Zeilen hinzu. */
    public void add(String line) {
        lines.add(line);
    }

    /** Fügt alle Zeilen des übergebenen Objektes zu diesem hinzu. */
    public void addAll(Lines that) {
        lines.addAll(that.getLines());
    }

    /** Fügt eine Liste von Zeilen zu diesem hinzu. */
    public void addAll(List<String> listOfLines) {
        lines.addAll(listOfLines);
    }

    /** Entfernt die Zeile am Angegebene (0-basierten) Index. */
    public String remove(int index) {
        return lines.remove(index);
    }

    @Override
    public Iterator<String> iterator() {
        return lines.iterator();
    }

    /** Getter für die Zeilen als Liste. */
    public List<String> getLines() {
        return lines;
    }

    /**
     * Gibt die Zeilen als einen String zurück, wobei die Zeilen jeweils mit einem Zeilenumbruch
     * verbunden werden.
     */
    public String joinWithLineBreak() {
        return Text.joinWithLineBreak(lines);
    }

    /** Gibt die Anzahl Zeilen zurück. */
    public int size() {
        return lines.size();
    }

    /** Gibt die Zeile mit dem übergebenen, Null-basierten Index zurück. */
    public String get(int index) {
        return lines.get(index);
    }

    /** Gibt an, ob die Menge der Zeilen leer ist. */
    public boolean isEmpty() {
        return lines.isEmpty();
    }

    /** Schreibt die Zeilen unter dem angegebenen Dateinamen raus. */
    public void writeToFile(String filename) {
        writeToFile(filename, Charset.ISO_8859_1);
    }

    /** Schreibt die Zeilen unter dem angegebenen Dateinamen und im angegebenen Encoding raus. */
    public void writeToFile(String filename, Charset charset) {
        FileHelper.writeLinesToFile(lines, filename, charset);
    }

    /** Entfernt die erste und letzte Zeile. */
    public void skipFirstAndLast() {
        if (lines.size() < 2) {
            throw new RuntimeException(
                    "Zu wenige Zeilen vorhanden, um die erste und letzte Zeile zu entfernen.");
        }
        lines.remove(lines.size() - 1);
        lines.remove(0);
    }

    /** Ermittelt die ungefähre Anzahl an Bytes. */
    public int countEstimatedByteLength() {
        int size = 2 * lines.size(); // Zeilenumbrüche
        for (String line : lines) {
            size += line.getBytes().length;
        }
        return size;
    }

    /** Gibt eine schlichte String-Repräsentation zurück. */
    @Override
    public String toString() {
        return toNiceString();
        //return "Lines [lines=" + lines + "]";
    }

    /** Gibt eine schöne String-Repräsentation zurück. */
    public String toNiceString() {
        return CollectionsHelper.listListNice(lines);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((lines == null) ? 0 : lines.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Lines other = (Lines) obj;
        if (lines == null) {
            if (other.lines != null)
                return false;
        }
        else if (!lines.equals(other.lines))
            return false;
        return true;
    }

    /**
     * Liest den Inhalt der Datei mit dem gegebenen Dateinamen in eine Liste seiner Zeilen und gibt
     * diese zurück.
     */
    public static Lines readFileToLines(String filename) {
        return new Lines(FileHelper.readFileToList(filename));
    }

    /**
     * Liest den Inhalt der Datei mit dem gegebenen Dateinamen und im angegebenen Encoding in eine
     * Liste seiner Zeilen und gibt diese zurück.
     */
    public static Lines readFileToLines(String filename, Charset charset) {
        return new Lines(FileHelper.readFileToList(filename, charset));
    }

    /** Splittet die Eingabe an Zeilenumbrüchen und erzeugt daraus ein Lines-Objekt. */
    public static Lines splitTextByLineBreaks(String text) {
        return new Lines(Text.splitByLineBreaks(text));
    }

    /** Erzeugt aus einer Liste von Listen von Strings eine Liste von Lines-Objekten. */
    public static List<Lines> createListOfLinesFromListOfListOfStrings(
            List<List<String>> listOfLists) {
        List<Lines> listOfLines = new ArrayList<>();
        for (List<String> list : listOfLists) {
            listOfLines.add(new Lines(list));
        }
        return listOfLines;
    }

    /** Macht aus einer Reihe von Strings ein Lines-Objekt. */
    public static Lines buildListFrom(String ... lines) {
        return new Lines(CollectionsHelper.buildListFrom(lines));
    }

    /**
     * Erzeugt aus den in einem String zusammengesetzten Zeilen (z.B. einer Datei) ein
     * Lines-Objekt.
     */
    public static Lines createFromMultipleLineInOneString(String line) {
        List<String> lines = Text.splitByLineBreaks(line);
        return new Lines(lines);
    }

}
