package de.duehl.basics.collections;

/*
 * Copyright 2019 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;

/**
 * Eine Klasse für eine Menge von Dingen, die ihre Reihenfolge beibehält.
 *
 * Achtung, diese Klasse bietet nicht die Geschwindigkeitsvorteile eines HashSets, kommt es darauf
 * an, sollte wohl OrderedNonOverwritingMap verwendet werden oder eine analoge Klasse für das
 * Set geschrieben werden!
 *
 * @version 1.01     2019-01-03
 * @author Christian Dühl
 */

public class OrderedSet<Element> implements Iterable<Element> {

    /** Liste der Elemente der Menge. */
    private final List<Element> elements;

    /**
     * Gibt an, ob das Einfügen von bereits vorhandenen Elementen ein Fehler ist (true), oder
     * stillschweigend übergangen wird (false).
     */
    private boolean multipleInsertedElementIsError;

    /** Konstruktor für eine leere Menge. */
    public OrderedSet() {
        elements = new ArrayList<>();
        multipleInsertedElementIsError = true;
    }

    /**
     * Wenn diese Methode aufgerufen wird, wird das mehrfache Einfügen des Elements nicht als
     * Fehler betrachtet sondern stillschweigend übergangen, ohne das Objekt noch einmal
     * hinzuzufügen.
     */
    public void ignoreMultipleInsertedElements() {
        multipleInsertedElementIsError = false;
    }

    /**
     * Fügt das Element hinzu.
     *
     * @param element
     *            Hinzuzufügendes Element.
     * @throws IllegalArgumentException
     *             Wenn es ein gleiches Element bereits in der Menge gibt.
     */
    public void add(Element element) {
        if (elements.contains(element)) {
            if (multipleInsertedElementIsError) {
                throw new IllegalArgumentException("Das Element " + element
                        + " ist bereits in der Menge enthalten!");
            }
        }
        else {
            elements.add(element);
        }
    }

    /** Gibt an, ob die Menge das übergebene Element enthält. */
    public boolean contains(Element element) {
        return elements.contains(element);
    }

    /**
     * Gibt einen Iterator über die Elemente der Menge zurück, mit dem die for-Schleife betrieben
     * werden kann.
     */
    @Override
    public Iterator<Element> iterator() {
        return elements.iterator();
    }

    /**
     * Entfernt das übergebene Element.
     *
     * @param element
     *            Das zu entfernende Element.
     * @throws IllegalArgumentException
     *             Wenn das Element nicht in der Menge gibt.
     */
    public void remove(Element element) {
        if (elements.contains(element)) {
            elements.remove(element);
        }
        else {
            throw new IllegalArgumentException("Das Element " + element
                    + " ist nicht in der Menge enthalten!");
        }
    }

    /** Gibt die Anzahl der Elemente in der Menge an. */
    public int size() {
        return elements.size();
    }

}
