package de.duehl.basics.io;

/*
 * Copyright 2016 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.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import de.duehl.basics.text.Text;

/**
 * Diese Klasse stellt eine Menge von Dateinamen dar.
 *
 * @version 1.01     2016-11-23
 * @author Christian Dühl
 */

public class FileSet implements Iterable<String>, Serializable  {

    private static final long serialVersionUID = 3045240775849454776L;

    /** Liste mit den Dateinamen. */
    private final List<String> fileNames;

    /**
     * Konstruktor.
     *
     * @param fileNames
     *            Liste mit den Dateinamen.
     * @throws IllegalArgumentException
     *             Wenn Dateinamen mehrfach vorkommen.
     */
    public FileSet(List<String> fileNames) {
        this.fileNames = new ArrayList<>();

        for (String fileName : fileNames) {
            add(fileName);
        }
    }

    /**
     * Konstruktor.
     *
     * @param fileNames
     *            Beliebig viele Dateinamen.
     * @throws IllegalArgumentException
     *             Wenn Dateinamen mehrfach vorkommen.
     */
    public FileSet(String ... fileNames) {
        this(Arrays.asList(fileNames));
    }

    /**
     * Fügt einen weiteren Dateinamen hinzu. Die Dateinamen werden dahingehend normalisiert, dass
     * aus allen Backslashes Slashes gemacht werden.
     *
     * @param fileName
     *            Dateiname.
     * @throws IllegalArgumentException
     *             Wenn die Datei schon in der Liste ist.
     */
    public void add(String fileName) {
        String normalizedFileName = normalize(fileName);
        if (fileNames.contains(normalizedFileName)) {
            throw new IllegalArgumentException("Die Datei " + normalizedFileName
                    + " ist schon im FileSet vorhanden!");
        }
        fileNames.add(normalizedFileName);
    }

    /**
     * Normalisiert einen Dateinamen dahingehend, dass aus allen Backslashes Slashes gemacht
     * werden.
     *
     * @param fileName
     *            Zu normalisierender Dateiname (z.B. C:\Temp\foo).
     * @return Normalisierter Dateiname (z.B. C:/Temp/foo).
     */
    private String normalize(String fileName) {
        return Text.allBackslashesToSlashes(fileName);
    }

    /**
     * Fügt alle Dateinamen aus einem anderen FileSet zu diesem hinzu, falls sie nicht schon
     * enthalten sind.
     *
     * @param otherFileSet
     *            Anderes FileSet, dessen Dateinamen diesem FilesSet hinzugefügt werden.
     */
    public void addAll(FileSet otherFileSet) {
        /*
         * files.addAll(otherFileSet.files);
         * geht leider nicht, weil dann ggf. doppelte Dateinamen ins Set kommen könnten.
         */

        for (String fileName : otherFileSet) {
            if (!fileNames.contains(fileName)) {
                fileNames.add(fileName);
            }
        }
    }

    /**
     * Gibt an, ob ein Dateiname im FileSet enthalten ist.
     *
     * @param fileName
     *            Zu überprüfender Dateiname.
     * @return true genau dann, wenn der Dateiname (normalisiert) im FileSet enthalten ist.
     */
    public boolean contains(String fileName) {
        return fileNames.contains(normalize(fileName));
    }

    /**
     * Gibt einen Iterator für die Liste zurück, damit diese in modernen
     * forEach-Schleifen der Art                                                 <br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;<tt>
     *
     *     for (String fileName : FileSet) {                                     <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
     *         doWomethingWith(fileName);                                        <br/>&nbsp;&nbsp;&nbsp;&nbsp;
     *     }                                                                     </tt><br/><br/>
     *
     *  verwendet werden kann.
     *
     *  @return Iterator über die interne Liste.
     */
    @Override
    public Iterator<String> iterator() {
        return fileNames.iterator();
    }

    /** Getter für die Anzahl der Dateinamen im FileSet. */
    public int size() {
        return fileNames.size();
    }

    /**
     * Getter für den Dateinamen am übergebenen Index
     *
     * @param index
     *            Index des Dateinamens in der Liste.
     * @return Element an der Stelle des Index.
     * @throws IndexOutOfBoundsException
     *             Wenn der Index außerhalb des zulässigen Bereiches zwischen 0 und Größe der Liste
     *             - 1 liegt.
     */
    public String get(int index) {
        return fileNames.get(index);
    }

    /** Stringrepräsentation. */
    @Override
    public String toString() {
        return "FileSet [fileNames=" + fileNames + "]";
    }

    /**
     * Entfernt den (normalisierten) Dateinamen aus der Liste.
     *
     * @param fileName
     *            Zu entfernenden Dateinamen.
     * @return true genau dann, wenn die Liste den Dateinamen enthielt.
     */
    public boolean remove(String fileName) {
        return fileNames.remove(normalize(fileName));
    }

    /** Entfernt alle Dateinamen aus der Liste. */
    public void clear() {
        fileNames.clear();
    }

    /** Gibt an, ob die Liste der Dateinamen leer ist. */
    public boolean isEmpty() {
        return fileNames.isEmpty();
    }

    /** Sortiert die Dateinamen alphanumerisch. */
    public void sort() {
        Collections.sort(fileNames);
    }

}
