/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.collections.shorts;

import java.util.*;

/**
 * Convenience subclass for short sets.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public abstract class AbstractShortSet extends AbstractShortCollection
                                      implements ShortSet {

    protected AbstractShortSet() {}

    public short min() {
        return Short.MIN_VALUE;
    }

    public short max() {
        return Short.MAX_VALUE;
    }

    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof ShortSet)) return false;
        ShortSet that = (ShortSet)obj;
        if (this.size() != that.size()) return false;   
        try {
            return containsAll(that);
        }
        catch (Exception ignore) {
            return false;
        }
    }

    public int hashCode() {
        int h = 0;
        for (ShortIterator itr = iterator(); itr.hasNext();) {
            h += hash(itr.next());
        }
        return h;
    }

    public boolean isEmpty() {
        return !iterator().hasNext();
    }

    public boolean addAll(ShortCollection c) {
        if (c instanceof ShortInterval) {
            ShortInterval r = (ShortInterval)c;
            return addInterval(r.first(), r.last());
        }

        if (c instanceof ShortSortedSet) {
            boolean modified = false;
            for (Iterator itr = ((ShortSortedSet)c).intervalIterator(); itr.hasNext();) {
                ShortInterval r = (ShortInterval)itr.next();
                modified |= addInterval(r.first(), r.last());
            }
            return modified;
        }

        return super.addAll(c);
    }

    public boolean removeAll(ShortCollection c) {
        if (c instanceof ShortInterval) {
            ShortInterval r = (ShortInterval)c;
            return removeInterval(r.first(), r.last());
        }

        if (c instanceof ShortSortedSet) {
            boolean modified = false;
            for (Iterator itr = ((ShortSortedSet)c).intervalIterator(); itr.hasNext();) {
                ShortInterval r = (ShortInterval)itr.next();
                modified |= removeInterval(r.first(), r.last());
            }
            return modified;
        }

        if (size() <= c.size()) {
            return super.removeAll(c);
        }

        boolean modified = false;
        for (ShortIterator itr = c.iterator(); itr.hasNext();) {
            modified |= remove(itr.next());
        }
        return modified;
    }

    public boolean retainAll(ShortCollection c) {
        if (c instanceof ShortInterval) {
            ShortInterval r = (ShortInterval)c;
            return retainInterval(r.first(), r.last());
        }
        return super.retainAll(c);
    }

    public boolean containsInterval(short first, short last) {
        if (first > last) return true;
        if (first == last) return contains(first);
        short min = min(), max = max();
        if (first < min || last > max) return false;
        short len = (short)(last-first+1);
        if (len > size()) return false;
        for (short e=first; e<=last; e++) {
            if (!contains(e)) return false;
        }
        return true;
    }

    public boolean addInterval(short first, short last) {
        short min = min(), max = max();
        if (first < min) first = min;
        if (last > max) last = max;
        boolean modified = false;
        for (short e = first; e<=last; e++) {
            modified |= add(e);
        }
        return modified;
    }

    public boolean removeInterval(short first, short last) {
        short min = min(), max = max();
        if (first < min) first = min;
        if (last > max) last = max;
        boolean modified = false;
        for (short e = first; e<=last; e++) {
            modified |= remove(e);
        }
        return modified;
    }

    public boolean retainInterval(short first, short last) {
        boolean modified = false;
        for (ShortIterator itr = iterator(); itr.hasNext();) {
            short e = itr.next();
            if (e >= first && e <= last) {
                itr.remove();
                modified = true;
            }
        }
        return modified;
    }

    public ShortSet complementSet() {
        return new ComplementView(this);
    }

    private final static int hash(short e) {
        return e;                                  
    }

    private static class ComplementView extends AbstractShortSet {
        final ShortSet base;
        ComplementView(ShortSet base) {
            this.base = base;
        }
        public short min() {
            return base.min();
        }
        public short max() {
            return base.max();
        }
        public boolean contains(short e) {
            return !base.contains(e);
        }
        public boolean add(short e) {
            return base.remove(e);
        }
        public boolean remove(short e) {
            return base.add(e);
        }
        public boolean addAll(ShortCollection c) {
            return base.removeAll(c);
        }
        public boolean removeAll(ShortCollection c) {
            return base.addAll(c);
        }
        public boolean addInterval(short first, short last) {
            return base.removeInterval(first, last);
        }
        public boolean removeInterval(short first, short last) {
            return base.addInterval(first, last);
        }
        public void clear() {
            base.addInterval(min(), max());
        }
        public ShortSet complementSet() {
            return base;
        }
        public ShortIterator iterator() {
            return new ComplementIterator(base, min(), max());
        }
    }

    private static class ComplementIterator implements ShortIterator {
        final ShortSet base;
        final short min, max;
        short next;
        short curr;
        ComplementIterator(ShortSet base, short min, short max) {
            this.base = base;
            this.min = min;
            this.max = max;
            this.next = min;
            fetchNext();
        }
        public boolean hasNext() {
            return next <= max;
        }
        public short next() {
            if (!hasNext()) throw new NoSuchElementException();
            curr = next++;
            fetchNext();
            return curr;
        }
        void fetchNext() {
            while (next <= max && base.contains(next)) next++;
        }
        public void remove() {
            base.add(curr);
        }
    }
}
