package de.sebse.fuplanner.tools;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public abstract class SortedList<T, I, F> implements Iterable<T>, Serializable {

    private final ArrayList<T> internalList = new ArrayList<>();

    public void add(T e) {
        internalList.add(e);
        Collections.sort(internalList, this::compare);
    }

    public void remove(int index) {
        this.internalList.remove(index);
    }

    protected abstract int compare(T o1, T o2);

    protected abstract boolean hasIdentifier(T o1, I id);

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    protected abstract boolean hasFilter(T o1, F filter);

    @Nullable
    public T get(int i) {
        return internalList.get(i);
    }

    @Nullable
    public T getById(I id) {
        for (T item : this.internalList) {
            if (hasIdentifier(item, id))
                return item;
        }
        return null;
    }

    public int size() {
        return internalList.size();
    }

    public boolean contains(T o1) {
        for (T o2 : this.internalList) {
            if (compare(o1, o2) == 0)
                return true;
        }
        return false;
    }

    @NonNull
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private int pos = -1;
            @Override
            public boolean hasNext() {
                return pos+1 < internalList.size();
            }

            @Override
            public T next() {
                pos++;
                if (pos < internalList.size())
                    return internalList.get(pos);
                return null;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("no changes allowed");
            }
        };
    }

    public Iterator<T> filteredIterator(F filter) {
        return new Iterator<T>() {
            private int index = -1;
            private int next = -1;
            @Override
            public boolean hasNext() {
                if (index==next)
                    predict();
                return next != -1;
            }

            @Override
            public T next() {
                if (index == next)
                    predict();
                if (next == -1)
                    return null;
                index = next;
                return internalList.get(index);
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("no changes allowed");
            }

            private void predict() {
                int size = internalList.size();
                do {
                    next++;
                } while (next < size && decline(internalList.get(next)));
                if (next == size)
                    next = -1;
            }

            private boolean decline(T ob){
                return !hasFilter(ob, filter);
            }
        };
    }

    @NonNull
    @Override
    public String toString() {
        return this.internalList.toString();
    }
}
