package Tools;

import java.io.*;

/**
 * A FilterWriter stream that inserts space after each linefeed to indent its
 * output.
 *
 * @author jonh@cs.dartmouth.edu
 */
public class IndentWriter
	extends FilterWriter {

	int depth;
	boolean needIndent = true;

	public IndentWriter(Writer out) {
		super(out);
	}

	/**
	 * Set the number of spaces to indent to an absolute value.
	 */
	public void setIndent(int depth) {
		this.depth = depth;
	}

	/**
	 * Adjust the number of spaces to indent by some (positive or
	 * negative) increment.
	 */
	public void addIndent(int increment) {
		depth += increment;
		if (depth<0) {
			depth = 0;
		}
	}

	public void write(int c)
		throws IOException {
		char cbuf[] = new char[1];
		cbuf[0] = (char) c;
		write(cbuf, 0, 1);
	}

	public void write(char[] cbuf, int off, int len)
		throws IOException {
		write(new String(cbuf), off, len);
	}

	public void write(String str, int off, int len)
		throws IOException {
		int nextCR = -1;
		int endPost = off+len;
		while ((nextCR = str.indexOf("\n", off)) >=0
			&& nextCR < endPost) {
			writeLine(str, off, nextCR-off+1);
			needIndent = true;
			off = nextCR+1;
		}
		if (off<endPost) {
			writeLine(str, off, endPost-off);
		}
	}

	protected void writeLine(String s, int off, int len)
		throws IOException {
		if (needIndent) {
			writeIndent();
			needIndent = false;
		}
		out.write(s, off, len);
	}

	/**
	 * Output <code>depth</code> spaces to indent a line.
	 */
	protected void writeIndent()
		throws IOException {
		char cbuf[] = new char[depth];
		java.util.Arrays.fill(cbuf, ' ');
		out.write(cbuf, 0, cbuf.length);
	}

	public static void main(String args[]) {
		try	{
			IndentWriter iw =
				new IndentWriter(new OutputStreamWriter(System.out));
			iw.write("This is\ntwo lines.\n");
			iw.addIndent(4);
			iw.write("This is\ntwo indented lines.\n");
			iw.addIndent(-2);
			iw.write("This is\ntwo less indented lines.\nand a partial line...");
			iw.addIndent(2);
			iw.write("This is\ntwo indented lines.\n");
			iw.flush();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	public void flush() {
		try {
			super.flush();
		} catch (IOException ex) {
		}
	}

	public void write(String s) {
		try {
			write(s, 0, s.length());
		} catch (IOException ex) {
			// PrintWriter sets an internal error flag and expects
			// the programmer to call it. Me? I'll just ignore it.
		}
	}

	public void print(String s) {
		write(s);
	}

	public void println() {
		print("\n");
		flush();
	}

	public void println(String s) {
		print(s);
		println();
	}
}
