package org.infinispan.commons.util;

import java.util.Arrays;
import java.util.Collection;
import java.util.NoSuchElementException;
import java.util.PrimitiveIterator;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
import java.util.function.IntPredicate;
import java.util.stream.IntStream;

/**
 * Read-only set representing all the integers from {@code 0} to {@code size - 1} (inclusive).
 *
 * @author Dan Berindei
 * @since 9.0
 * @deprecated since 9.3 This class will no longer be public. Please use {@link IntSets#immutableRangeSet(int)} instead.
 */
@Deprecated
public class RangeSet implements IntSet {
   final int size;

   public RangeSet(int size) {
      this.size = size;
   }

   @Override
   public int size() {
      return size;
   }

   @Override
   public boolean isEmpty() {
      return size <= 0;
   }

   @Override
   public boolean contains(Object o) {
      if (!(o instanceof Integer))
         return false;
      int i = (int) o;
      return contains(i);
   }

   @Override
   public boolean contains(int i) {
      return 0 <= i && i < size;
   }

   @Override
   public PrimitiveIterator.OfInt iterator() {
      return new RangeSetIterator(size);
   }

   @Override
   public int[] toIntArray() {
      int[] array = new int[size];
      for (int i = 0; i < size; i++) {
         array[i] = i;
      }
      return array;
   }

   @Override
   public byte[] toBitSet() {
      if (size == 0) {
         return Util.EMPTY_BYTE_ARRAY;
      }
      int offset = (size >>> 3);
      if ((size & 0xf) == 0) {
         byte[] array = new byte[offset];
         Arrays.fill(array, (byte) 0xff);
         return array;
      }
      byte[] array = new byte[offset + 1];
      if (offset > 0) {
         Arrays.fill(array, 0, offset, (byte) 0xff);
      }
      int lastBitOffset = size > 8 ? size % 8 : size;
      array[offset] = (byte) (0xff >> (8 - lastBitOffset));
      return array;
   }

   @Override
   public Object[] toArray() {
      Object[] array = new Object[size];
      for (int i = 0; i < size; i++) {
         array[i] = i;
      }
      return array;
   }

   @Override
   public <T> T[] toArray(T[] a) {
      T[] array = a.length >= size ? a :
                  (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
      for (int i = 0; i < size; i++) {
         array[i] = (T) Integer.valueOf(i);
      }
      return array;
   }

   @Override
   public boolean add(Integer integer) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean remove(Object o) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean remove(int i) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean containsAll(Collection<?> c) {
      if (c instanceof IntSet) {
         return containsAll((IntSet) c);
      }
      for (Object o : c) {
         if (!contains(o))
            return false;
      }
      return true;
   }

   @Override
   public boolean containsAll(IntSet set) {
      if (set instanceof RangeSet) {
         return size >= ((RangeSet) set).size;
      }
      PrimitiveIterator.OfInt iter = set.iterator();
      while (iter.hasNext()) {
         if (!contains(iter.nextInt())) {
            return false;
         }
      }
      return true;
   }

   @Override
   public boolean add(int i) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   public void set(int i) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean addAll(IntSet set) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean addAll(Collection<? extends Integer> c) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean retainAll(Collection<?> c) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean retainAll(IntSet c) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean removeAll(IntSet set) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public boolean removeAll(Collection<?> c) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public void clear() {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

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

      if (o instanceof RangeSet) {
         RangeSet integers = (RangeSet) o;

         return size == integers.size;
      } else {
         Set set = (Set) o;
         return size == set.size() && containsAll(set);
      }
   }

   @Override
   public IntStream intStream() {
      return IntStream.range(0, size);
   }

   @Override
   public void forEach(IntConsumer action) {
      for (int i = 0; i < size; ++i) {
         action.accept(i);
      }
   }

   @Override
   public void forEach(Consumer<? super Integer> action) {
      for (int i = 0; i < size; ++i) {
         // Has cost of auto boxing, oh well
         action.accept(i);
      }
   }

   @Override
   public Spliterator.OfInt intSpliterator() {
      return new RangeSetSpliterator(size);
   }

   @Override
   public boolean removeIf(IntPredicate filter) {
      throw new UnsupportedOperationException("RangeSet is immutable");
   }

   @Override
   public int hashCode() {
      return size;
   }

   @Override
   public String toString() {
      return "RangeSet(" + size + ")";
   }

   private static class RangeSetSpliterator implements Spliterator.OfInt {
      private int next;
      private final int size;

      public RangeSetSpliterator(int size) {
         this.next = 0;
         this.size = size;
      }

      RangeSetSpliterator(int next, int size) {
         this.next = next;
         this.size = size;
      }

      @Override
      public OfInt trySplit() {
         int lo = next, mid = (lo + size) >>> 1;
         return (lo >= mid)
               ? null
               : new RangeSetSpliterator(lo, next = mid);
      }

      @Override
      public void forEachRemaining(IntConsumer action) {
         for (; next < size; ++next) {
            action.accept(next);
         }
      }

      @Override
      public long estimateSize() {
         return size - next;
      }

      @Override
      public int characteristics() {
         return SIZED | SUBSIZED | DISTINCT | SORTED | ORDERED | NONNULL | IMMUTABLE;
      }

      @Override
      public boolean tryAdvance(IntConsumer action) {
         if (next < size) {
            action.accept(next++);
            return true;
         }
         return false;
      }
   }

   private static class RangeSetIterator implements PrimitiveIterator.OfInt {
      private int size;
      private int next;

      public RangeSetIterator(int size) {
         this.size = size;
         this.next = 0;
      }

      @Override
      public boolean hasNext() {
         return next < size;
      }

      @Override
      public int nextInt() {
         if (next >= size) {
            throw new NoSuchElementException();
         }
         return next++;
      }

      @Override
      public Integer next() {
         return nextInt();
      }

      @Override
      public void remove() {
         throw new UnsupportedOperationException("RangeSet is read-only");
      }
   }
}
