/*
 * 02/24/2004
 *
 * DocumentReader.java - A reader for javax.swing.text.Document
 * objects.
 * 
 * This library is distributed under a modified BSD license.  See the included
 * RSyntaxTextArea.License.txt file for details.
 */
package com.fr.design.gui.syntax.io;

import java.io.Reader;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Segment;


/**
 * A <code>Reader</code> for <code>javax.swing.text.Document</code> objects.
 *
 * @author Robert Futrell
 * @version 1.0
 */
public class DocumentReader extends Reader {

	/**
	 * The stream's position in the document.
	 */
	private long position;
	
	/**
	 * A remembered position in the document.
	 */
	private long mark;
	
	/**
	 * The document we're working on.
	 */
	private Document document;
	
	/**
	 * Used for fast character access.
	 */
	private Segment segment;


	/**
	 * Constructor.
	 *
	 * @param document The document we're 'reading'.
	 */
	public DocumentReader(Document document) {
		position = 0;
		mark = -1;
		this.document = document;
		this.segment = new Segment();
	}


	/**
	 * This currently does nothing...
	 */
	@Override
	public void close() {
	}


	/**
	 * Marks the present position in the stream.  Subsequent calls to
	 * <code>reset()</code> will reposition the stream to this point.
	 *
	 * @param readAheadLimit Ignored.
	 */
	@Override
	public void mark(int readAheadLimit) {
		mark = position;
	}


	/**
	 * Tells whether this reader supports the <code>mark</code> operation.
	 * This always returns <code>true</code> for <code>DocumentReader</code>.
	 */
	@Override
	public boolean markSupported() {
		return true;
	}


	/**
	 * Reads the single character at the current position in the document.
	 */
	@Override
	public int read() {
		if(position>=document.getLength()) {
			return -1;      // Read past end of document.
		}
		try {
			document.getText((int)position,1, segment);
			position++;
			return segment.array[segment.offset];
		} catch (BadLocationException ble) {
			/* Should never happen?? */
			ble.printStackTrace();
			return -1;
		}
	}


	/**
	 * Read <code>array.length</code> characters from the beginning
	 * of the document into <code>array</code>.
	 *
	 * @param array The array to read characters into.
	 * @return The number of characters read.
	 */
	@Override
	public int read(char array[]) {
		return read(array, 0, array.length);
	}


	/**
	 * Reads characters into a portion of an array.
	 *
	 * @param cbuf The destination buffer.
	 * @param off Offset at which to start storing characters.
	 * @param len Maximum number of characters to read.
	 * @return The number of characters read, or <code>-1</code> if the
	 *         end of the stream (document) has been reached.
	 */
	@Override
	public int read(char cbuf[], int off, int len) {
		int k;
		if(position>=document.getLength()) {
			return -1;      // Read past end of document.
		}
		k = len;
		if((position+k)>=document.getLength())
			k = document.getLength() - (int)position;
		if(off + k >= cbuf.length)
			k = cbuf.length - off;
		try {
			document.getText((int)position, k, segment);
			position += k;
			System.arraycopy(segment.array,segment.offset,
							cbuf,off,
							k);
			return k;
		} catch (BadLocationException ble) {
			/* Should never happen ? */
			return -1;
		}
	}


	/**
	 * Tells whether this reader is ready to be read without
	 * blocking for input.  <code>DocumentReader</code> will
	 * always return true.
	 *
	 * @return <code>true</code> if the next read operation will
	 *         return without blocking.
	 */
	@Override
	public boolean ready() {
		return true;
	}


	/**
	 * Resets the stream.  If the stream has been marked, then attempt to
	 * reposition it at the mark.  If the stream has not been marked, then
	 * move it to the beginning of the document.
	 */
	@Override
	public void reset() {
		if(mark==-1) {
			position = 0;
		}
		else {
			position = mark;
			mark = -1;
		}
	}


	/**
	 * Skips characters.  This will not 'skip' past the end of the document.
	 *
	 * @param n The number of characters to skip.
	 * @return The number of characters actually skipped.
	 */
	@Override
	public long skip(long n) {
		if (position+n<=document.getLength()) {
			position += n;
			return n;
		}
		long temp = position;
		position = document.getLength();
		return document.getLength() - temp;
	}


	/**
	 * Move to the specified position in the document.  If <code>pos</code>
	 * is greater than the document's length, the stream's position is moved
	 * to the end of the document.
	 *
	 * @param pos The position in the document to move to.
	 */
	public void seek(long pos) {
		position = Math.min(pos, document.getLength());
	}


}