package org.omnetpp.common.collections;

/**
 *  This class represents an immutable non-empty range of consecutive int values.
 */
public class IntRange implements Comparable<IntRange> {
    private final int first;
    private final int last;

    public IntRange(int first, int last) {
        if (last < first)
            throw new IllegalArgumentException("invalid range bounds");
        this.first = first;
        this.last = last;
    }

    public int first() {
        return first;
    }

    public int last() {
        return last;
    }

    public int length() {
        return last - first + 1;
    }

    public boolean intersects(IntRange other) {
        return (first >= other.first && first <= other.last)
            || (other.first >= first && other.first <= last);
    }

    public boolean adjacentTo(IntRange range) {
        return last+1 == range.first || range.last+1 == first;
    }

    public boolean canMergeWith(IntRange range) {
        return intersects(range) || adjacentTo(range);
    }

    public IntRange mergeWith(IntRange range) {
        if (!canMergeWith(range))
            throw new RuntimeException("cannot merge ranges");
        return quickMergeWith(range);
    }

    private IntRange quickMergeWith(IntRange range) {
        int nfirst = first < range.first ? first : range.first;
        int nlast = last > range.last ? last : range.last;
        return new IntRange(nfirst, nlast);
    }

    public IntRange tryMergeWith(IntRange range) {
        if (!canMergeWith(range))
            return null;
        return quickMergeWith(range);
    }

    public boolean contains(int v) {
        return v >= first && v <= last;
    }

    public int intersectionLength(IntRange other) {
        if (other.contains(first)) {
            int end = Math.min(last, other.last);
            return end - first + 1;
        } else if (this.contains(other.first)) {
            int end = Math.min(last, other.last);
            return end - other.first + 1;
        } else {
            return 0;
        }
    }

    public boolean equals(Object obj) {
        if (!(obj instanceof IntRange))
            return false;
        IntRange range = (IntRange)obj;
        return first == range.first && last == range.last;
    }

    public int compareTo(IntRange range) {
        if (first < range.first)
            return -1;
        if (first > range.first)
            return 1;
        if (last < range.last)
            return -1;
        if (last > range.last)
            return 1;
        return 0;
    }

    public int hashCode() {
        return first ^ last;
    }

    public String toString() {
        return "" + first + "-" + last;
    }
}