"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection = exports.LinkedCollection = void 0;
const StringBuilder_1 = require("../../../java/lang/StringBuilder");
const IndexOutOfBoundsException_1 = require("../../../java/lang/IndexOutOfBoundsException");
const LinkedCollectionElement_1 = require("../../../core/adt/collection/LinkedCollectionElement");
const NullPointerException_1 = require("../../../java/lang/NullPointerException");
const Collection_1 = require("../../../java/util/Collection");
const JavaObject_1 = require("../../../java/lang/JavaObject");
const LinkedCollectionDescendingIterator_1 = require("../../../core/adt/collection/LinkedCollectionDescendingIterator");
const LinkedCollectionIterator_1 = require("../../../core/adt/collection/LinkedCollectionIterator");
const Arrays_1 = require("../../../java/util/Arrays");
const NoSuchElementException_1 = require("../../../java/util/NoSuchElementException");
class LinkedCollection extends JavaObject_1.JavaObject {
    _head = null;
    _tail = null;
    _size = 0;
    _modCount = 0;
    /**
     * Implementation for method overloads of 'constructor'
     */
    constructor(__param0) {
        super();
        if ((typeof __param0 === "undefined")) {
            this._head = null;
            this._tail = null;
            this._size = 0;
            this._modCount = 0;
        }
        else if (((typeof __param0 !== "undefined") && ((__param0 instanceof JavaObject_1.JavaObject) && (__param0.isTranspiledInstanceOf('de.nrw.schule.svws.core.adt.collection.LinkedCollection'))) || (__param0 === null))) {
            let c = cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection(__param0);
            this._size = 0;
            this._modCount = 0;
            let iter = c.iterator();
            while (iter.hasNext())
                this.add(iter.next());
            this._modCount = c._modCount;
        }
        else
            throw new Error('invalid method overload');
    }
    size() {
        return this._size;
    }
    isEmpty() {
        return (this._head === null) ? true : false;
    }
    contains(obj) {
        if (this.isEmpty())
            return false;
        let iter = this.iterator();
        while (iter.hasNext())
            if (JavaObject_1.JavaObject.equalsTranspiler(iter.next(), (obj)))
                return true;
        return false;
    }
    iterator() {
        return new LinkedCollectionIterator_1.LinkedCollectionIterator(this);
    }
    /**
     * Implementation for method overloads of 'toArray'
     */
    toArray(__param0) {
        if ((typeof __param0 === "undefined")) {
            if (this._size === 0)
                return Array(0).fill(null);
            let array = Array(this._size).fill(null);
            let iter = this.iterator();
            for (let i = 0; i < this._size; i++) {
                array[i] = iter.next();
            }
            return array;
        }
        else if (((typeof __param0 !== "undefined") && Array.isArray(__param0))) {
            let a = __param0;
            if (a.length < this._size)
                return this.toArray();
            let iter = this.iterator();
            for (let i = 0; i < this._size; i++) {
                let e = iter.next();
                a[i] = e;
            }
            Arrays_1.Arrays.fill(a, this._size, a.length, null);
            return a;
        }
        else
            throw new Error('invalid method overload');
    }
    add(e) {
        if (e === null)
            return false;
        let newElem = new LinkedCollectionElement_1.LinkedCollectionElement(e, null, null);
        if ((this._head === null) || (this._tail === null)) {
            this._head = newElem;
            this._tail = newElem;
            this._size++;
            this._modCount++;
            return true;
        }
        newElem.setPrev(this._tail);
        this._tail.setNext(newElem);
        this._tail = newElem;
        this._size++;
        this._modCount++;
        return true;
    }
    /**
     * Entfernt das übergebene Element.
     *
     * @param elem   das zu entfernende Element
     *
     * @return true, falls das Element erfolgreich entfernt wurde, und false, falls null übergeben wurde.
     */
    removeElement(elem) {
        if (elem === null)
            return false;
        let prev = elem.getPrev();
        let next = elem.getNext();
        if (this._size === 1) {
            this._head = null;
            this._tail = null;
        }
        else if (JavaObject_1.JavaObject.equalsTranspiler(elem, (this._head))) {
            if (next === null)
                throw new NullPointerException_1.NullPointerException();
            this._head = next;
            next.setPrev(null);
        }
        else if (JavaObject_1.JavaObject.equalsTranspiler(elem, (this._tail))) {
            if (prev === null)
                throw new NullPointerException_1.NullPointerException();
            this._tail = prev;
            prev.setNext(null);
        }
        else {
            if ((next === null) || (prev === null))
                throw new NullPointerException_1.NullPointerException();
            next.setPrev(prev);
            prev.setNext(next);
        }
        this._size--;
        this._modCount++;
        return true;
    }
    /**
     * Implementation for method overloads of 'remove'
     */
    remove(__param0) {
        if (((typeof __param0 !== "undefined") && ((__param0 instanceof Object) || ((__param0 instanceof JavaObject_1.JavaObject) && (__param0.isTranspiledInstanceOf('java.lang.Object')))) || (__param0 === null))) {
            let obj = (__param0 instanceof JavaObject_1.JavaObject) ? (0, JavaObject_1.cast_java_lang_Object)(__param0) : __param0;
            if (this.isEmpty())
                return false;
            return this.removeElement(this.findFirst(obj));
        }
        else if ((typeof __param0 === "undefined")) {
            let value = this.poll();
            if (value === null)
                throw new NoSuchElementException_1.NoSuchElementException();
            return value;
        }
        else
            throw new Error('invalid method overload');
    }
    containsAll(c) {
        if ((c === null) || (this === c))
            return true;
        for (let o of c)
            if (!this.contains(o))
                return false;
        return true;
    }
    addAll(c) {
        if ((c === null) || (c.size() === 0))
            return false;
        if (((c instanceof JavaObject_1.JavaObject) && (c.isTranspiledInstanceOf('de.nrw.schule.svws.core.adt.collection.LinkedCollection')))) {
            let coll = cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection(c);
            if ((coll._tail === null) || (coll._head === null))
                throw new NullPointerException_1.NullPointerException();
            let last = coll._tail;
            let current = coll._head;
            this.add(current.getValue());
            while (current !== last) {
                current = current.getNext();
                if (current === null)
                    throw new NullPointerException_1.NullPointerException();
                this.add(current.getValue());
            }
            return true;
        }
        let result = false;
        for (let elem of c) {
            if (this.add(elem))
                result = true;
        }
        return result;
    }
    removeAll(c) {
        if (c === null)
            return false;
        if (this === c) {
            if (this.size() === 0)
                return false;
            this.clear();
            return true;
        }
        let result = false;
        for (let o of c) {
            if (this.remove(o)) {
                result = true;
                while (this.remove(o))
                    ;
            }
        }
        return result;
    }
    retainAll(c) {
        if ((this === c) || (this._head === null))
            return false;
        if (c === null) {
            this.clear();
            return true;
        }
        let iter = this.iterator();
        let tmp = new LinkedCollection();
        while (iter.hasNext()) {
            let elem = iter.next();
            if (!c.contains(elem))
                tmp.add(elem);
        }
        if (tmp.isEmpty())
            return false;
        return this.removeAll(tmp);
    }
    clear() {
        this._head = null;
        this._tail = null;
        this._size = 0;
        this._modCount++;
    }
    hashCode() {
        let hashCode = 1;
        for (let e of this)
            hashCode = 31 * hashCode + (e === null ? 0 : JavaObject_1.JavaObject.getTranspilerHashCode(e));
        return hashCode;
    }
    equals(obj) {
        if ((obj === null) || (!(((obj instanceof JavaObject_1.JavaObject) && (obj.isTranspiledInstanceOf('java.util.Collection'))))))
            return false;
        let other = (0, Collection_1.cast_java_util_Collection)(obj);
        if (this._size !== other.size())
            return false;
        let iter = this.iterator();
        let otherIter = other.iterator();
        while (iter.hasNext()) {
            if (!JavaObject_1.JavaObject.equalsTranspiler(iter.next(), (otherIter.next())))
                return false;
        }
        return true;
    }
    clone() {
        return new LinkedCollection(this);
    }
    toString() {
        let sb = new StringBuilder_1.StringBuilder();
        sb.append("[");
        let iter = this.iterator();
        while (iter.hasNext()) {
            sb.append(iter.next());
            if (iter.hasNext() === true)
                sb.append(", ");
        }
        sb.append("]");
        return sb.toString();
    }
    /**
     * Diese Methode ist eine Hilfsmethode für die Methode sort(). Sie mischt die beiden über die prev-Zeiger
     * verketteten Listen left und right zu einer kombinierten, über die prev-Zeiger verketteten Liste.
     *
     * @param comparator   ein {@link Comparator} zum Vergleichen zweier Elemente
     * @param left         die erste sortierte Liste
     * @param right        die zweite sortierte Liste
     *
     * @return die kombinierte sortierte Liste
     */
    merge(comparator, left, right) {
        let headTo;
        let headFrom;
        if (comparator.compare(left.getValue(), right.getValue()) > 0) {
            headFrom = left;
            headTo = right;
        }
        else {
            headFrom = right;
            headTo = left;
        }
        let target = headTo;
        while (headFrom !== null) {
            let current = headFrom;
            headFrom = headFrom.getPrev();
            let targetPrev = target.getPrev();
            while ((targetPrev !== null) && (comparator.compare(targetPrev.getValue(), current.getValue()) < 0)) {
                target = targetPrev;
                targetPrev = target.getPrev();
            }
            if (targetPrev === null) {
                target.setPrev(current);
                break;
            }
            current.setPrev(targetPrev);
            target.setPrev(current);
        }
        return headTo;
    }
    /**
     * Sortiert den Inhalte dieser Liste mithilfe des übergebenen {@link Comparator}-Objekts.
     *
     * @param comparator   ein {@link Comparator} zum Vergleichen zweier Elemente
     *
     * @return true, falls eine Sortierung erfolgreich war
     */
    sort(comparator) {
        if (comparator === null)
            return false;
        if ((this._size <= 1) || (this._head === null) || (this._tail === null))
            return true;
        this._modCount++;
        for (let current = this._head; current !== null; current = current.getNext())
            current.setPrev(null);
        while (this._head !== null) {
            let left = this._head;
            let right = left.getNext();
            if (right === null)
                throw new NullPointerException_1.NullPointerException();
            this._head = right.getNext();
            left.setNext(null);
            right.setNext(null);
            let sorted = this.merge(comparator, left, right);
            this._tail.setNext(sorted);
            this._tail = sorted;
        }
        this._head = this._tail;
        this._tail.setNext(this._tail.getPrev());
        while (this._tail.getPrev() !== null) {
            this._tail = this._tail.getPrev();
            if (this._tail === null)
                throw new NullPointerException_1.NullPointerException();
            this._tail.setNext(this._tail.getPrev());
        }
        this._head.setPrev(null);
        let current = this._head;
        let next = current.getNext();
        while (next !== null) {
            next.setPrev(current);
            current = next;
            next = current.getNext();
        }
        this._modCount++;
        return true;
    }
    /**
     * Sucht das Element an der Stelle Index.
     *
     * @param index   die Stelle des gesuchten Elements
     *
     * @return das Element an der gesuchten Stelle
     *
     * @throws IndexOutOfBoundsException   wenn der Index nicht im gültigen Bereich liegt (index >= 0) && (index < size()))
     */
    find(index) {
        if ((index < 0) || (index >= this._size))
            throw new IndexOutOfBoundsException_1.IndexOutOfBoundsException();
        let current = this._head;
        for (let i = 0; (current !== null); i++, current = current.getNext())
            if (i === index)
                return current;
        throw new IndexOutOfBoundsException_1.IndexOutOfBoundsException();
    }
    /**
     * Sucht ein LinkedCollectionElement in der Collection mit dem Wert obj
     * und gibt es zurück
     *
     * @param obj   der Wert der in der LinkedCollection gesucht werden soll
     *
     * @return  ein LinkedCollectionElement<E> falls der Wert in der Collection
     * 			enthalten ist und das Element dessen , ansonsten null
     */
    findFirst(obj) {
        if (obj === null)
            return null;
        let current = this._head;
        while (current !== null) {
            if (JavaObject_1.JavaObject.equalsTranspiler(current.getValue(), (obj)))
                return current;
            current = current.getNext();
        }
        return null;
    }
    /**
     * Sucht ein LinkedCollectionElement in der Collection mit dem Wert obj
     * und gibt es zurück
     *
     * @param obj   der Wert der in der LinkedCollection gesucht werden soll
     *
     * @return  ein LinkedCollectionElement<E> falls der Wert in der Collection
     * 			enthalten ist und das Element dessen, ansonsten null
     */
    findLast(obj) {
        if (obj === null)
            return null;
        let current = this._tail;
        while (current !== null) {
            if (JavaObject_1.JavaObject.equalsTranspiler(current.getValue(), (obj)))
                return current;
            current = current.getPrev();
        }
        return null;
    }
    offer(e) {
        return this.add(e);
    }
    poll() {
        if (this._head === null)
            return null;
        let value = this._head.getValue();
        this._head = this._head.getNext();
        if (this._head === null)
            this._tail = null;
        else
            this._head.setPrev(null);
        this._size--;
        this._modCount++;
        return value;
    }
    element() {
        if (this._head === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return this._head.getValue();
    }
    peek() {
        return (this._head === null) ? null : this._head.getValue();
    }
    addFirst(e) {
        if (e === null)
            throw new NullPointerException_1.NullPointerException();
        let newElem = new LinkedCollectionElement_1.LinkedCollectionElement(e, null, null);
        if ((this._head === null) || (this._tail === null)) {
            this._head = newElem;
            this._tail = newElem;
        }
        else {
            newElem.setNext(this._head);
            this._head.setPrev(newElem);
            this._head = newElem;
        }
        this._size++;
        this._modCount++;
    }
    addLast(e) {
        if (e === null)
            throw new NullPointerException_1.NullPointerException();
        this.add(e);
    }
    offerFirst(e) {
        this.addFirst(e);
        return true;
    }
    offerLast(e) {
        this.addLast(e);
        return true;
    }
    removeFirst() {
        let value = this.poll();
        if (value === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return value;
    }
    removeLast() {
        let value = this.pollLast();
        if (value === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return value;
    }
    pollFirst() {
        return this.poll();
    }
    pollLast() {
        if (this._tail === null)
            return null;
        let value = this._tail.getValue();
        this._tail = this._tail.getPrev();
        if (this._tail === null)
            this._head = null;
        else
            this._tail.setNext(null);
        this._size--;
        this._modCount++;
        return value;
    }
    getFirst() {
        if (this._head === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return this._head.getValue();
    }
    getLast() {
        if (this._tail === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return this._tail.getValue();
    }
    peekFirst() {
        return (this._head === null) ? null : this._head.getValue();
    }
    peekLast() {
        return (this._tail === null) ? null : this._tail.getValue();
    }
    removeFirstOccurrence(obj) {
        if (this.isEmpty())
            return false;
        return this.removeElement(this.findFirst(obj));
    }
    removeLastOccurrence(obj) {
        if (this.isEmpty())
            return false;
        return this.removeElement(this.findLast(obj));
    }
    push(e) {
        this.addFirst(e);
    }
    pop() {
        let value = this.poll();
        if (value === null)
            throw new NoSuchElementException_1.NoSuchElementException();
        return value;
    }
    descendingIterator() {
        return new LinkedCollectionDescendingIterator_1.LinkedCollectionDescendingIterator(this);
    }
    /**
     * Gibt den Wert an der Stelle index zurück.
     *
     * @param index   der Index
     *
     * @return der Wert
     *
     * @throws IndexOutOfBoundsException   wenn der Index nicht im gültigen Bereich liegt {@code (index >= 0) && (index < size()))}
     */
    get(index) {
        return this.find(index).getValue();
    }
    /**
     * Ersetzt den Wert an der Stelle index mit dem neuen übergebenen Wert.
     *
     * @param index     die Stelle, wo der Wert ersetzt werden soll
     * @param element   der neue Wert für die Stelle
     *
     * @return der alte Wert an der Stelle
     *
     * @throws IndexOutOfBoundsException   wenn der Index nicht im gültigen Bereich liegt {@code (index >= 0) && (index < size()))}
     */
    set(index, element) {
        return this.find(index).setValue(element);
    }
    isTranspiledInstanceOf(name) {
        return ['java.lang.Cloneable', 'java.util.Collection', 'java.util.Queue', 'java.util.Deque', 'java.lang.Iterable', 'de.nrw.schule.svws.core.adt.collection.LinkedCollection'].includes(name);
    }
    [Symbol.iterator]() {
        let iter = this.iterator();
        const result = {
            next() {
                if (iter.hasNext())
                    return { value: iter.next(), done: false };
                return { value: null, done: true };
            }
        };
        return result;
    }
}
exports.LinkedCollection = LinkedCollection;
function cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection(obj) {
    return obj;
}
exports.cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection = cast_de_nrw_schule_svws_core_adt_collection_LinkedCollection;
//# sourceMappingURL=LinkedCollection.js.map