/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.collection.lists;

import java.io.Serializable;
import java.util.Collection;
import java.util.RandomAccess;
import net.ranides.assira.collection.IntCollection;
import net.ranides.assira.collection.arrays.NativeArrayAllocator;
import net.ranides.assira.collection.iterators.IntListIterator;
import net.ranides.assira.collection.sets.IntHashSet;
import net.ranides.assira.collection.sets.IntSet;

/**
 * Abstract class for easy creation "virtual lists". Algorithms in below implementation
 * try to minimize use of {@link #get} method, because of assumption that read operations
 * have high cost, although class doesn't use any buffering.
 * <p>
 * By default list is immutable. If you want to create mutable list, you shoud override:
 * </p>
 * <ul>
 *      <li> {@link #set(int index, Object value) set(int index, Object value)}</li>
 *      <li> {@link #add(int index, Object value) add(int index, Object value)}</li>
 *      <li> {@link #remove(int) remove(int index)}</li>
 * </ul>
 * @param <T>
 */
public abstract class IntVirtualList extends AIntList implements RandomAccess, Serializable {
    
    private static final long serialVersionUID = 1L;

    private static final String NSE = "Not supported by immutable virtual lists.";

    @Override
    public abstract int size();

    @Override
    public abstract int getInt(int index);

    @Override
    public void add(int index, int element) {
        throw new UnsupportedOperationException(NSE);
    }

    @Override
    public int set(int index, int element) {
        throw new UnsupportedOperationException(NSE);
    }

    @Override
    public int removeInt(int index) {
        throw new UnsupportedOperationException(NSE);
    }

    @Override
    public boolean containsAll(Collection<?> values) {
        // we don't use this.contains to avoid iteration over list many times
        IntSet set = iset(values, true);
        return values.size()==set.size() && icontainsAll(set);
    }
    
    @Override
    public boolean containsAll(IntCollection values) {
        // we don't use this.contains to avoid iteration over list many times
        return icontainsAll(new IntHashSet(values));
    }
    
    @Override
    public boolean removeAll(Collection<?> values) {
        // we don't use this.remove to avoid iteration over list many times
        return iremoveAll(iset(values, false));
    }
    
    @Override
    public boolean removeAll(IntCollection values) {
        // we don't use this.remove to avoid iteration over list many times
        return iremoveAll(new IntHashSet(values));
    }

    @Override
    public boolean retainAll(Collection<?> values) {
        // we don't use this.remove&this.contains to avoid iteration over list many times
        return iretainAll(iset(values, false));
    }
    
    @Override
    public boolean retainAll(IntCollection values) {
        // we don't use this.remove&this.contains to avoid iteration over list many times
        return iretainAll(new IntHashSet(values));
    }
    
    private boolean iretainAll(IntSet set) {
        boolean modified = false;
        IntListIterator iterator = listIterator();
        while(iterator.hasNext()) {
            int value = iterator.nextInt();
            if(!set.contains(value)) {
                iterator.remove();
                modified = true;
            }
        }
        return modified;
    }
    
    private boolean icontainsAll(IntSet set) {
        IntListIterator iterator = listIterator();
        while(iterator.hasNext()) {
            set.remove( iterator.nextInt());
            if( set.isEmpty() ) { return true; }
        }
        return set.isEmpty();
    }
    
    private boolean iremoveAll(IntSet set) {
        boolean modified = false;
        IntListIterator iterator = listIterator();
        while(iterator.hasNext()) {
            int value = iterator.nextInt();
            if(set.contains(value)) {
                iterator.remove();
                modified = true;
            }
        }
        return modified;
    }
    
    private IntSet iset(Collection<?> values, boolean failfast) {
        IntSet set = new IntHashSet(values.size());
        for(Object o : values) {
            if(o instanceof Integer) {
                set.add( ((Integer)o).intValue() );
            } else if(failfast) {
                break;
            }
        }
        return set;
    }
    
    @Override
    public IntList subList(int begin, int end) {
        // we use "our implementation" because we want to provide overriden #containsAll / #removeAll etc
        return new SubList(this, begin, end);
    }

    private static class SubList extends IntVirtualList {

        private final IntList list;
        private final int begin;
        private int end;

        public SubList(IntList list, int begin, int end) {
			NativeArrayAllocator.ensureFromTo(list.size(), begin, end);
            this.list = list;
            this.begin = begin;
            this.end = end;
        }

        @Override
        public int size() {
            return Math.max(0, Math.min(end, list.size() ) - begin );
        }

        @Override
        public int getInt(int index) {
            return list.get(begin+index);
        }

        @Override
        public void add(int index, int element) {
            list.add(begin+index,element);
			end++;
        }

        @Override
        public int set(int index, int element) {
            return list.set(begin+index,element);
        }

        @Override
        public int removeInt(int index) {
            int prev = list.remove(begin + index);
			end--;
			return prev;
        }

    }
   
}
