/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.apache.geode.cache.lucene.internal.directory;

import java.io.EOFException;
import java.io.IOException;

import org.apache.lucene.store.IndexInput;

import org.apache.geode.cache.lucene.internal.filesystem.File;
import org.apache.geode.cache.lucene.internal.filesystem.SeekableInputStream;

class FileIndexInput extends IndexInput {

  private final File file;
  SeekableInputStream in;
  private long position;

  // Used for slice operations
  private long sliceOffset;
  private long sliceLength;

  FileIndexInput(String resourceDesc, File file) {
    this(resourceDesc, file, 0L, file.getLength());
  }

  /**
   * Constructor for a slice.
   */
  private FileIndexInput(String resourceDesc, File file, long offset, long length) {
    super(resourceDesc);
    this.file = file;
    in = file.getInputStream();
    this.sliceOffset = offset;
    this.sliceLength = length;
  }

  @Override
  public long length() {
    return sliceLength;
  }

  @Override
  public void close() throws IOException {
    in.close();
  }

  @Override
  public FileIndexInput clone() {
    FileIndexInput clone = (FileIndexInput) super.clone();
    clone.in = in.clone();
    return clone;
  }

  @Override
  public long getFilePointer() {
    return position;
  }

  @Override
  public void seek(long pos) throws IOException {
    in.seek(pos + sliceOffset);
    this.position = pos;
  }

  @Override
  public IndexInput slice(String sliceDescription, long offset, long length) throws IOException {
    if (length > (this.sliceLength - offset)) {
      throw new IllegalArgumentException("Slice length is to large. Asked for " + length
          + " file length is " + sliceLength + ": " + this.file.getName());
    }
    if (offset < 0 || offset >= this.sliceLength) {
      throw new IllegalArgumentException("Slice offset is invalid: " + this.file.getName());
    }

    FileIndexInput result =
        new FileIndexInput(sliceDescription, file, sliceOffset + offset, length);
    result.seek(0);
    return result;
  }

  @Override
  public byte readByte() throws IOException {
    if (++position > sliceLength) {
      throw new EOFException("Read past end of file " + file.getName());
    }

    int result = in.read();
    if (result == -1) {
      throw new EOFException("Read past end of file " + file.getName());
    } else {
      return (byte) result;
    }
  }

  @Override
  public void readBytes(byte[] b, int offset, int len) throws IOException {
    if (len == 0) {
      return;
    }

    if (position + len > sliceLength) {
      throw new EOFException("Read past end of file " + file.getName());
    }

    // For the FileSystemInputStream, it will always read all bytes, up
    // until the end of the file. So if we didn't get enough bytes, it's
    // because we reached the end of the file.
    int numRead = in.read(b, offset, len);
    if (numRead < len) {
      throw new EOFException("Read past end of file " + file.getName());
    }

    position += len;
  }
}
