package net.onrc.onos.core.util;

import com.google.common.base.Objects;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * A class representing an ID space. This class is not thread-safe.
 */
public final class IdBlock {
    private final long start;
    private final long size;

    private long currentId;

    /**
     * Constructs a new ID block with the specified size and initial value.
     *
     * @param start initial value of the block
     * @param size size of the block
     * @throws IllegalArgumentException if the size is less than or equal to 0
     */
    public IdBlock(long start, long size) {
        checkArgument(size > 0, "size should be more than 0, but %s", size);

        this.start = start;
        this.size = size;

        this.currentId = start;
    }

    // TODO: consider if this method is needed or not
    /**
     * Returns the initial value.
     *
     * @return initial value
     */
    public long getStart() {
        return start;
    }

    // TODO: consider if this method is needed or not
    /**
     * Returns the last value.
     *
     * @return last value
     */
    public long getEnd() {
        return start + size - 1;
    }

    /**
     * Returns the block size.
     *
     * @return block size
     */
    public long getSize() {
        return size;
    }

    /**
     * Returns the next ID in the block.
     *
     * @return next ID
     * @throws UnavailableIdException if there is no available ID in the block.
     */
    public long getNextId() {
        if (currentId > getEnd()) {
            throw new UnavailableIdException(String.format(
                    "use all IDs in allocated space (size: %d, end: %d, current: %d)",
                    size, getEnd(), currentId
            ));
        }
        long id = currentId;
        currentId++;

        return id;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        IdBlock that = (IdBlock) o;
        return Objects.equal(this.start, that.start)
                && Objects.equal(this.size, that.size)
                && Objects.equal(this.currentId, that.currentId);
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(start, size, currentId);
    }

    @Override
    public String toString() {
        return Objects.toStringHelper(getClass())
                .add("start", start)
                .add("size", size)
                .add("currentId", currentId)
                .toString();
    }
}

@@ -27,16 +27,112 @@
 .util;%0A%0A
+import java.util.concurrent.atomic.AtomicLong;%0A%0Aimport javax.annotation.concurrent.ThreadSafe;%0A%0A
 import c
@@ -274,43 +274,24 @@
 ace.
- This class is not t
+%0A */%0A@T
 hread
--s
+S
 afe
-.%0A */
 %0Apub
@@ -388,17 +388,23 @@
 private 
-l
+AtomicL
 ong curr
@@ -875,21 +875,37 @@
 entId = 
+new AtomicLong(
 start
+)
 ;%0A    %7D%0A
@@ -1649,20 +1649,66 @@
     
-if (currentI
+final long id = currentId.getAndIncrement();%0A        if (i
 d %3E 
@@ -1803,16 +1803,17 @@
     %22use
+d
  all IDs
@@ -1903,24 +1903,17 @@
 tEnd(), 
-currentI
+i
 d%0A      
@@ -1932,24 +1932,25 @@
       %7D%0A
+%0A
         
 long id 
@@ -1945,76 +1945,106 @@
     
-long id = currentId;%0A        currentId++;%0A%0A        return id;%0A    %7D%0A
+return id;%0A    %7D%0A%0A    // TODO: Do we really need equals and hashCode? Should it contain currentId?
 %0A   
@@ -2435,16 +2435,22 @@
 urrentId
+.get()
 , that.c
@@ -2449,32 +2449,38 @@
 , that.currentId
+.get()
 );%0A    %7D%0A%0A    @O

Make IdBlock thread safe.