import ceylon.collection {
    ListMutator
}

import java.lang {
    IllegalArgumentException,
    UnsupportedOperationException,
    IllegalStateException,
    overloaded
}
import java.util {
    AbstractList,
    Collection,
    Iterator
}

"A Java [[java.util::List]] that wraps a Ceylon [[List]].
 This list is unmodifiable, throwing 
 [[java.lang::UnsupportedOperationException]] from mutator 
 methods."
shared class JavaList<E>(List<E?> list)
        extends AbstractList<E>() {
    
    get(Integer int) => list[int];
    
    size() => list.size;

    iterator() => object satisfies Iterator<E> {
        variable value delegate 
                = JavaIterator(list.iterator());
        variable value currentIndex = -1;

        hasNext() => delegate.hasNext();
        
        shared actual E? next() {
            currentIndex++;
            return delegate.next();
        }
        
        shared actual void remove() {
            if (!is ListMutator<Nothing> list) {
                throw UnsupportedOperationException("not a mutable list");
            }
            if (currentIndex < 0) {
                throw IllegalStateException();
            }
            list.delete(currentIndex--);
            delegate = JavaIterator(list.skip(currentIndex).iterator());
        }
    };

    shared actual E? set(Integer index, E? e) {
        if (exists e) {
            if (is ListMutator<E> list) {
                value result = list[index];
                list[index] = e;
                return result;
            }
            else {
                throw UnsupportedOperationException("not a mutable list");
            }
        }
        else {
            if (is ListMutator<Null> list) {
                value result = list[index];
                list[index] = null;
                return result;
            }
            else {
                if (list is ListMutator<Nothing>) {
                    throw IllegalArgumentException("list may not have null elements");
                }
                else {
                    throw UnsupportedOperationException("not a mutable list");
                }
            }
        }
    }

    shared actual overloaded Boolean add(E? e) {
        if (exists e) {
            if (is ListMutator<E> list) {
                list.add(e);
            }
            else {
                throw UnsupportedOperationException("not a mutable list");
            }
        }
        else {
            if (is ListMutator<Null> list) {
                list.add(null);
            }
            else {
                if (list is ListMutator<Nothing>) {
                    throw IllegalArgumentException("list may not have null elements");
                }
                else {
                    throw UnsupportedOperationException("not a mutable list");
                }
            }
        }
        return true;
    }
    
    shared actual overloaded void add(Integer index, E? e) {
        if (exists e) {
            if (is ListMutator<E> list) {
                list.insert(index, e);
            }
            else {
                throw UnsupportedOperationException("not a mutable list");
            }
        }
        else {
            if (is ListMutator<Null> list) {
                list.insert(index, null);
            }
            else {
                if (list is ListMutator<Nothing>) {
                    throw IllegalArgumentException("list may not have null elements");
                }
                else {
                    throw UnsupportedOperationException("not a mutable list");
                }
            }
        }
    }
    
    shared actual Boolean remove(Object? e) {
        if (is ListMutator<E> list) {
            if (is E? e) {
                if (exists e) {
                    return list.removeFirst(e);
                }
                else {
                    throw IllegalArgumentException("cannot remove null elements");
                }
            }
            else {
                return false;
            }
        }
        else {
            throw UnsupportedOperationException("not a mutable list");
        }
    }
    
    shared actual Boolean removeAll(Collection<out Object> collection) {
        if (is ListMutator<E> list) {
            variable Boolean result = false;
            for (e in collection) {
                if (is E e, list.removeFirst(e)) {
                    result = true;
                }
            }
            return result;
        }
        else {
            throw UnsupportedOperationException("not a mutable list");
        }
    }
    
    shared actual Boolean retainAll(Collection<out Object> collection) {
        if (is ListMutator<E> list) {
            variable Boolean result = false;
            for (e in list.clone()) { //TODO: is the clone really necessary?
                if (exists e, //TODO: what to do with nulls, this is sorta wrong?
                    !e in collection) {
                    list.removeFirst(e);
                    result = true;
                }
            }
            return result;
        }
        else {
            throw UnsupportedOperationException("not a mutable list");
        }
    }
    
    shared actual void clear() {
        if (is ListMutator<Nothing> list) {
            list.clear();
        }
        else {
            throw UnsupportedOperationException("not a mutable list");
        }
    }
}