/*
 * LpBuffer.java
 *
 * Copyright (C) 2006 - 2007 Martin Slota
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 51
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/*
 * History:
 * v1.0.0 (2007-05-04):
 * - no version information kept before :P
 *
 * PENDING create a junit test
 */

package lp.struct.util;

import java.io.StringWriter;
import lp.struct.LpStructureUnit;

/**
 * A convenience class that makes it possible to work with classes inheriting
 * from a {@link LpPrinter} for writing into a string buffer instead of a
 * writer. The contents of the buffer are available through the methods of this
 * character sequence.
 *
 * @author Martin Slota
 * @version 1.0.0
 * @see LpPrinter
 * @see LpPrettyPrinter#getBuffer()
 * @see EvolpPrettyPrinter#getBuffer()
 * @see LpHtmlPrinter#getBuffer()
 * @see LpEncoder#getBuffer()
 */
public class LpBuffer implements Appendable, CharSequence {
	/**
	 * The {@link LpPrinter} used to append {@link LpStructureUnit}s to
	 * {@link #buffer}.
	 */
	private final LpPrinter<StringWriter> printer;
	
	/**
	 * The delegate for manipulating a mutable string.
	 */
	private final StringBuffer buffer;
	
	
	/**
	 * Creates a new instance of {@code LpBuffer} that uses {@code printer} to
	 * append usual objects and also {@link LpStructureUnit}s to a string
	 * buffer.
	 *
	 * @param printer will be used to append usual objects and also
	 * {@link LpStructureUnit}s to a string buffer. Shouldn't be manipulated by
	 * any other object.
	 */
	public LpBuffer(LpPrinter<StringWriter> printer) {
		this.printer = printer;
		buffer = printer.getOut().getBuffer();
	}
	
	/**
	 * Appends the specified character sequence to this {@code LpBuffer}.
	 *
	 * @param csq the {@code CharSequence} that should be appended to this
	 * {@code LpBuffer}
	 * @return a reference to this object after appending
	 */
	public LpBuffer append(CharSequence csq) {
		printer.append(csq);
		return this;
	}
	
	/**
	 * Appends a subsequence of the specified character sequence to this
	 * {@code LpBuffer}.
	 *
	 * @param csq the character sequence from which a subsequence will be
	 * appended. If {@code csq} is {@code null}, then characters
	 * will be appended as if {@code csq} contained the four
	 * characters {@code "null"}.
	 * @param start the index of the first character in the subsequence
	 * @param end the index of the character following the last character in the
	 * subsequence
	 * @return a reference to this object after appending
	 */
	public LpBuffer append(CharSequence csq, int start, int end) {
		printer.append(csq, start, end);
		return this;
	}
	
	/**
	 * Appends the specified character to this {@code LpBuffer}.
	 *
	 * @param c the character to append
	 * @return a reference to this object after appending
	 */
	public LpBuffer append(char c) {
		printer.append(c);
		return this;
	}
	
	/**
	 * Returns the length (character count) of this character sequence.
	 *
	 * @return the length of the sequence of characters currently represented by
	 * this object
	 * @see CharSequence#length()
	 */
	public int length() {
		return buffer.length();
	}
	
	/**
	 * Returns the {@code char} value in this sequence at the specified index.
	 *
	 * @param index the index of the character that should be returned. Indexing
	 * starts at 0, highest allowed value is {@code length() - 1}
	 * @throws IndexOutOfBoundsException if {@code index} if negative or greater
	 * or equal to {@code length()}
	 * @see #length()
	 * @see CharSequence#charAt(int)
	 */
	public char charAt(int index) {
		return buffer.charAt(index);
	}
	
	/**
	 * Returns a subsequence of the character sequence represented by this
	 * object.
	 *
	 * @param start the start index (inclusive)
	 * @param end the end index (exclusive)
	 * @throws IndexOutOfBoundsException if either of the indexes is negative,
	 * or {@code end} is greater than {@code length()}, or {@code start} is
	 * greater than {@code end}
	 * @see CharSequence#subSequence(int, int)
	 */
	public CharSequence subSequence(int start, int end) {
		return buffer.subSequence(start, end);
	}
	
	
	/**
	 * Deletes all characters in this sequence.
	 */
	public void reset() {
		buffer.setLength(0);
	}
	
	/**
	 * Appends a textual representation of {@code unit} to this
	 * {@code LpBuffer}. The string is constructed in the {@code #visit()}
	 * methods of this class. If {@code unit} is {@code null}, the string "null"
	 * is appended.
	 *
	 * @param unit the {@code LpStructureUnit} that should be represented
	 * as a string and appended to this character sequence
	 * @return a reference to this object after appending
	 *
	 */
	public LpBuffer append(LpStructureUnit unit) {
		printer.append(unit);
		return this;
	}
	
	/**
	 * The call to this method is identical to calling
	 *
	 *<pre>
	 *reset();
	 *append(unit);
	 *return toString();
	 *</pre>
	 *
	 * @param unit the {@code LpStructureUnit} that should be represented
	 * as a string and appended to this character sequence
	 */
	public String asString(LpStructureUnit unit) {
		reset();
		append(unit);
		return toString();
	}
	
	/**
	 * Returns the contents of this character sequence as a string.
	 *
	 * @return as specified above
	 */
	@Override
	public String toString() {
		return buffer.toString();
	}
}