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

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

import java.util.*;

/**
 * Convenience subclass for long sets.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public abstract class AbstractLongSet extends AbstractLongCollection
                                      implements LongSet {

    protected AbstractLongSet() {}

    public long min() {
        return Long.MIN_VALUE;
    }

    public long max() {
        return Long.MAX_VALUE;
    }

    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (!(obj instanceof LongSet)) return false;
        LongSet that = (LongSet)obj;
        if (this.size64() != that.size64()) return false; // PREPROC: Long,Int only
//        if (this.size() != that.size()) return false;   // PREPROC: except Long,Int
        try {
            return containsAll(that);
        }
        catch (Exception ignore) {
            return false;
        }
    }

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

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

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

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

        return super.addAll(c);
    }

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

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

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

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

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

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

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

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

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

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

    private final static int hash(long e) {
        return (int)((e & 0xffffffff) ^ (e >>> 32)); // PREPROC: Long only
//        return e;                                  // PREPROC: except Long
    }

    private static class ComplementView extends AbstractLongSet {
        final LongSet base;
        ComplementView(LongSet base) {
            this.base = base;
        }
        public long min() {
            return base.min();
        }
        public long max() {
            return base.max();
        }
        public long size64() {                         // PREPROC: Int,Long only
            return max() - min() + 1 - base.size64();  // PREPROC: Int,Long only
        }                                              // PREPROC: Int,Long only
        public boolean contains(long e) {
            return !base.contains(e);
        }
        public boolean add(long e) {
            return base.remove(e);
        }
        public boolean remove(long e) {
            return base.add(e);
        }
        public boolean addAll(LongCollection c) {
            return base.removeAll(c);
        }
        public boolean removeAll(LongCollection c) {
            return base.addAll(c);
        }
        public boolean addInterval(long first, long last) {
            return base.removeInterval(first, last);
        }
        public boolean removeInterval(long first, long last) {
            return base.addInterval(first, last);
        }
        public void clear() {
            base.addInterval(min(), max());
        }
        public LongSet complementSet() {
            return base;
        }
        public LongIterator iterator() {
            return new ComplementIterator(base, min(), max());
        }
    }

    private static class ComplementIterator implements LongIterator {
        final LongSet base;
        final long min, max;
        long next;
        long curr;
        ComplementIterator(LongSet base, long min, long max) {
            this.base = base;
            this.min = min;
            this.max = max;
            this.next = min;
            fetchNext();
        }
        public boolean hasNext() {
            return next <= max;
        }
        public long 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);
        }
    }
}
