package org.apache.lucene.store;

/*
 * 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.
 */

import java.io.IOException;

/** Base implementation class for buffered {@link IndexOutput}. */
public abstract class BufferedIndexOutput extends IndexOutput {
  public static final int DEFAULT_BUFFER_SIZE = 16384;

  private final int bufferSize;
  private final byte[] buffer;
  private long bufferStart = 0;           // position in file of buffer
  private int bufferPosition = 0;         // position in buffer

  /**
   * Creates a new {@link BufferedIndexOutput} with the default buffer size
   * {@value BufferedIndexOutput#DEFAULT_BUFFER_SIZE}
   */
  public BufferedIndexOutput() {
    this(DEFAULT_BUFFER_SIZE);
  }
  
  /**
   * Creates a new {@link BufferedIndexOutput} with the given buffer size. 
   * @param bufferSize the buffer size in bytes used to buffer writes internally.
   * @throws IllegalArgumentException if the given buffer size is less or equal to <tt>0</tt>
   */
  public BufferedIndexOutput(int bufferSize) {
    if (bufferSize <= 0) {
      throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
    }
    this.bufferSize = bufferSize;
    buffer = new byte[bufferSize];
  }

  @Override
  public void writeByte(byte b) throws IOException {
    if (bufferPosition >= bufferSize)
      flush();
    buffer[bufferPosition++] = b;
  }

  @Override
  public void writeBytes(byte[] b, int offset, int length) throws IOException {
    int bytesLeft = bufferSize - bufferPosition;
    // is there enough space in the buffer?
    if (bytesLeft >= length) {
      // we add the data to the end of the buffer
      System.arraycopy(b, offset, buffer, bufferPosition, length);
      bufferPosition += length;
      // if the buffer is full, flush it
      if (bufferSize - bufferPosition == 0)
        flush();
    } else {
      // is data larger then buffer?
      if (length > bufferSize) {
        // we flush the buffer
        if (bufferPosition > 0)
          flush();
        // and write data at once
        flushBuffer(b, offset, length);
        bufferStart += length;
      } else {
        // we fill/flush the buffer (until the input is written)
        int pos = 0; // position in the input data
        int pieceLength;
        while (pos < length) {
          pieceLength = (length - pos < bytesLeft) ? length - pos : bytesLeft;
          System.arraycopy(b, pos + offset, buffer, bufferPosition, pieceLength);
          pos += pieceLength;
          bufferPosition += pieceLength;
          // if the buffer is full, flush it
          bytesLeft = bufferSize - bufferPosition;
          if (bytesLeft == 0) {
            flush();
            bytesLeft = bufferSize;
          }
        }
      }
    }
  }

  @Override
  public void flush() throws IOException {
    flushBuffer(buffer, bufferPosition);
    bufferStart += bufferPosition;
    bufferPosition = 0;
  }

  /** Expert: implements buffer write.  Writes bytes at the current position in
   * the output.
   * @param b the bytes to write
   * @param len the number of bytes to write
   */
  private void flushBuffer(byte[] b, int len) throws IOException {
    flushBuffer(b, 0, len);
  }

  /** Expert: implements buffer write.  Writes bytes at the current position in
   * the output.
   * @param b the bytes to write
   * @param offset the offset in the byte array
   * @param len the number of bytes to write
   */
  protected abstract void flushBuffer(byte[] b, int offset, int len) throws IOException;
  
  @Override
  public void close() throws IOException {
    flush();
  }

  @Override
  public long getFilePointer() {
    return bufferStart + bufferPosition;
  }

  @Override
  public void seek(long pos) throws IOException {
    flush();
    bufferStart = pos;
  }

  @Override
  public abstract long length() throws IOException;


}

@@ -990,16 +990,91 @@
 utput %7B%0A
+  /** The default buffer size in bytes (%7B@value #DEFAULT_BUFFER_SIZE%7D). */%0A
   public
@@ -1408,24 +1408,25 @@
 ze%0A   * 
+(
 %7B@value 
 Buffered
@@ -1417,35 +1417,55 @@
 %7B@value 
-BufferedIndexOutput
+#DEFAULT_BUFFER_SIZE%7D bytes see %7B@link 
 #DEFAULT
@@ -1477,16 +1477,17 @@
 ER_SIZE%7D
+)
 %0A   */%0A 

fix javadocs