/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2006 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.io;


import java.io.IOException;
import java.io.Writer;

import java.lang.CharSequence;
import java.lang.IllegalStateException;
import java.lang.UnsupportedOperationException;
import javolution.lang.Reusable;
import java.lang.Appendable;
import javolution.text.Text;

/**
 * <p> This class allows any <code>Appendable</code> to be used as 
 *     a writer.</p>
 *
 * @author  <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
 * @version 3.8, May 8, 2006
 */
public final class AppendableWriter extends Writer implements Reusable {

    /**
     * Holds the current appendable output or <code>null</code> if closed.
     */
    private Appendable _output;

    /**
     * Creates a new appendable writer for which the appendable output 
     * is not set.
     * 
     * @see #setOutput(Appendable)
     */
    public AppendableWriter() {
    }

    /**
     * Sets the appendable output being written to.
     * For example:[code]
     *     Writer writer = new AppendableWriter().setOutput(new TextBuilder());
     * [/code]
     *
     * @param  output the appendable written to.
     * @return this writer.
     * @throws IllegalStateException if this writer is being reused and 
     *         it has not been {@link #close closed} or {@link #reset reset}.
     */
    public AppendableWriter setOutput(Appendable output) {
        if (_output != null)
            throw new IllegalStateException("Writer not closed or reset");
        _output = output;
        return this;
    }

    /**
     * Writes a single character.
     *
     * @param  c <code>char</code> the character to be written.
     * @throws IOException if an I/O error occurs.
     */
    public void write(char c) throws IOException {
        if (_output == null)
            throw new IOException("Writer closed");
        _output.append(c);
    }

    /**
     * Writes the 16 low-order bits of the given integer value;
     * the 16 high-order bits are ignored.
     *
     * @param  c the value of the character to be written.
     * @throws IOException if an I/O error occurs.
     */
    public void write(int c) throws IOException {
        if (_output == null)
            throw new IOException("Writer closed");
        _output.append((char) c);
    }

    /**
     * Writes a portion of an array of characters.
     *
     * @param  cbuf the array of characters.
     * @param  off the offset from which to start writing characters.
     * @param  len the number of characters to write.
     * @throws IOException if an I/O error occurs.
     */
    public void write(char cbuf[], int off, int len) throws IOException {
        if (_output == null)
            throw new IOException("Writer closed");
        _tmpBuffer = cbuf;
        _output.append(_tmpBufferAsCharSequence, off, off + len);
        _tmpBuffer = null; // Removes temporary references.
    }

    private char[] _tmpBuffer;

    private final CharSequence _tmpBufferAsCharSequence = new CharSequence() {
        public int length() {
            return _tmpBuffer.length;
        }

        public char charAt(int index) {
            return _tmpBuffer[index];
        }

        public CharSequence subSequence(int start, int end) {
            throw new UnsupportedOperationException();
        }
    };

    /**
     * Writes a portion of a string.
     *
     * @param  str a String.
     * @param  off the offset from which to start writing characters.
     * @param  len the number of characters to write.
     * @throws IOException if an I/O error occurs
     */
    public void write(String str, int off, int len) throws IOException {
        if (_output == null)
            throw new IOException("Writer closed");
        Object obj = str;
        if (obj instanceof CharSequence) {
            _output.append((CharSequence) obj);
        } else {
            _output.append(Text.valueOf(str));
        }
    }

    /**
     * Writes the specified character sequence.
     *
     * @param  csq the character sequence.
     * @throws IOException if an I/O error occurs
     */
    public void write(CharSequence csq) throws IOException {
        if (_output == null)
            throw new IOException("Writer closed");
        _output.append(csq);
    }

    /**
     * Flushes the stream.
     */
    public void flush() {
        // Do nothing (no buffer).
    }

    /**
     * Closes and {@link #reset resets} this writer for reuse.
     */
    public void close() {
        if (_output != null) {
            reset();
        }
    }

    // Implements Reusable.
    public void reset() {
        _output = null;
        _tmpBuffer = null;
    }
}