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

import java.util.*;

/**
 * Klasa reprezentująca złączenie wielu list jako jedną kolekcję.
 * @param <T>
 * @author ranides
 */
public class JoinList<T> extends AbstractSequentialList<T> {

    private final List<List<T>> content;

    /**
     * Tworzy nowe złączenie, do którego można doklejać nowe listy za pomocą
     * metody {@link #join(List) join(List)}
     */
    public JoinList() {
        content = new LinkedList<>();
    }
    
    public JoinList(List<T> values[]) {
        content = Arrays.asList(values);
    }

    /**
     * Tworzy nowe złączenie na podstawie podanej listy list.
     * @param values
     */
    public JoinList(List<List<T>> values) {
        content = values;
    }

    @Override
    public int size() {
        int size = 0;
        for(List<T> list : content) {
            size += list.size();
        }
        return size;
    }

    @Override
    public boolean contains(Object value) {
        for(List<T> list : content) {
            if( list.contains(value) ) { return true; }
        }
        return false;
    }

    @Override
    public T get(int index) {
        int begin, end = 0;

        for(List<T> list : content) {
            begin = end;
            end += list.size();
            if(index < end) {
                return list.get(index - begin);
            }
        }
        throw new IndexOutOfBoundsException("Index: " + index + " Size: " + end );
    }

    @Override
    public Iterator<T> iterator() {
        return new JoinIterator();
    }

    @Override
    public ListIterator<T> listIterator() {
        return new JoinIterator();
    }

    @Override
    public ListIterator<T> listIterator(int index) {
        return new JoinIterator(index);
    }

    /**
     * Dodaje do kolekcji kolejną listę. Uwaga: dodawana jest <b>lista</b>, a nie
     * jej elementy.
     * @param list
     * @return this
     */
    public JoinList<T> join(List<T> list) {
        content.add(list);
        return this;
    }

    private final class JoinIterator implements ListIterator<T> {

        private int segment = 0;
        private ListIterator<T> iterator;

        private int index = 0;
        private boolean hasNext = false;
        private boolean hasPrev = false;
        private T next;
        private T prev;

        public JoinIterator(int index) {
            this();
            while(index > 0) { next(); }
        }

        public JoinIterator() {
            segment = 0;
            iterator = content.get(segment).listIterator();
        }

        @Override
        public boolean hasNext() {
            if( hasNext ) {
                return true;
            }

            if( iterator!=null && iterator.hasNext() ) {
                next = iterator.next();
                hasNext = true;
                hasPrev = false;
                return true;
            }

            while( segment+1 < content.size() ) {
                hasPrev = false;
                iterator = content.get( ++segment ).listIterator();
                if(iterator.hasNext()) {
                    next = iterator.next();
                    hasNext = true;
                    return true;
                }
            }

            hasNext = false;
            return false;
        }

        @Override
        public T next() {
            if( hasNext() ) {
                hasNext = false;
                index++;
                return next;
            }
            throw new NoSuchElementException();
        }

        @Override
        public boolean hasPrevious() {
            if( hasPrev ) {
                return true;
            }

            if( iterator!=null && iterator.hasPrevious() ) {
                prev = iterator.previous();
                hasPrev = true;
                hasNext = false;
                return true;
            }

            while( segment > 0 ) {
                hasNext = false;
                List<T> list = content.get(--segment);
                iterator = list.listIterator(list.size());
                if(iterator.hasPrevious()) {
                    prev = iterator.previous();
                    hasPrev = true;
                    return true;
                }
            }

            hasPrev = false;
            return false;
        }

        @Override
        public T previous() {
            if( hasPrevious() ) {
                hasPrev = false;
                index--;
                return prev;
            }
            throw new NoSuchElementException();
        }

        @Override
        public int nextIndex() {
            return index;
        }

        @Override
        public int previousIndex() {
            return index-1;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("Immutable list.");
        }

        @Override
        public void set(T value) {
            throw new UnsupportedOperationException("Immutable list.");
        }

        @Override
        public void add(T value) {
            throw new UnsupportedOperationException("Immutable list.");
        }

    }
}
