package org.infinispan.lucene.impl;

import java.io.IOException;

import org.apache.lucene.store.IndexInput;

/**
 * Wraps a buffer to expose only a slice of it.
 * The buffer is not copied to avoid copy operations as the slice is expected to have a lifespan shorter than the buffer itself,
 * so there would be no benefit in having a smaller copy in heap.
 *
 * @author Sanne Grinovero &lt;sanne@hibernate.org&gt; (C) 2014 Red Hat Inc.
 * @since 7.0
 */
final class SlicedBufferIndexInput extends IndexInput {

   private final byte[] buffer;
   private final int offset;
   private final int length;
   private final int hardLimit;
   private int bufferPosition;

   protected SlicedBufferIndexInput(String resourceDescription, byte[] buffer, long offset, long length) {
      super(resourceDescription);
      this.buffer = buffer;
      this.offset = toInt(offset);
      this.length = toInt(length);
      this.hardLimit = this.offset + this.length;
      this.bufferPosition = this.offset;
      if (hardLimit > buffer.length) {
         throw new IllegalArgumentException("offset or length too large for the size of this buffer");
      }
   }

   @Override
   public void close() throws IOException {
      //no-op
   }

   @Override
   public long getFilePointer() {
      return bufferPosition - offset;
   }

   @Override
   public void seek(long pos) throws IOException {
      //Lucene might use positions larger than length(), in
      //this case you have to position the pointer to eof.
      bufferPosition = Math.min(hardLimit, ((int)pos) + offset);
   }

   @Override
   public long length() {
      return this.length;
   }

   public IndexInput slice(String sliceDescription, long offset, long length) throws IOException {
      return new SlicedBufferIndexInput(sliceDescription, buffer, offset + this.offset, length);
   }

   @Override
   public byte readByte() throws IOException {
      if (bufferPosition >= hardLimit) {
         throw new IOException("Read past EOF");
      }
      return buffer[bufferPosition++];
   }

   @Override
   public void readBytes(byte[] b, int offset, int len) throws IOException {
      if (hardLimit - bufferPosition < len) {
         throw new IOException("Read past EOF");
      }
      System.arraycopy(buffer, bufferPosition, b, offset, len);
      bufferPosition+=len;
   }

   private static int toInt(final long n) {
      if ( n < 0 || n > Integer.MAX_VALUE) {
         throw new IllegalArgumentException("A SlicedBufferIndexInput can only be defined with offset and length which are positive and fit in an int");
      }
      return (int) n;
   }

}
