package de.renew.guiprompt;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;


/**
 * A plain-text <code>Document</code> that updates its contents to capture
 * all output from a stream. The document waits for input until a
 * {@link IOException} occurs or it is terminated explicitly.
 * <p>
 * If a thread writing to the stream dies (<code>IOException: Write end
 * dead</code>), the <code>de.renew.guiprompt.ResponseDocument</code> goes to sleep until
 * {@link #revive} is called.
 * </p>
 *
 * Created: Mon Jun  6  2005
 *
 * @author Michael Duvigneau
 **/
public class ResponseDocument extends PlainDocument {
    /**
     * Logger used for debug and error output.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(ResponseDocument.class);

    /**
     * Buffered reader to receive data from the connected stream.
     */
    private final BufferedReader _reader;

    /**
     * Indicating whether the document has been terminated.
     */
    private boolean _terminated = false;
    /**
     * Monitor object used to pause and resume the reading thread.
     */
    private Object _monitor = new Object();
    /**
     * Thread responsible for reading the input stream and updating the document.
     */
    private Thread _responseReaderThread;

    /**
     * Creates a new <code>de.renew.guiprompt.ResponseDocument</code> listening to the given stream.
     *
     * @param stream  the <code>PipedOutputStream</code> where the document
     *                should connect to.
     *
     * @exception IOException if the initial connection to the stream fails.
     **/
    public ResponseDocument(final PipedOutputStream stream) throws IOException {
        super();
        this._reader = new BufferedReader(new InputStreamReader(new PipedInputStream(stream)));
        _responseReaderThread = new Thread() {
            @Override
            public void run() {
                while (!_terminated) {
                    try {
                        appendText(_reader.readLine());
                    } catch (IOException e) {
                        if ("Write end dead".equals(e.getMessage())
                            || "Pipe broken".equals(e.getMessage())) {
                            appendText("\n----\n");
                            synchronized (_monitor) {
                                try {
                                    _monitor.wait();
                                } catch (InterruptedException e2) {
                                    LOGGER.debug("Feedback thread interrupted.");
                                    appendText("\nFeedback thread interrupted.");
                                    terminate();
                                }
                            }
                        } else {
                            LOGGER.debug("Feedback exception: " + e, e);
                            appendText("\nFeedback exception: " + e);
                            terminate();
                        }
                    }
                }
                appendText("\nFeedback terminated. Press [Clear] to reinitialize.");
            }
        };
        _responseReaderThread.setName("GuiPrompt feedback reader thread");
        _responseReaderThread.start();
    }

    /**
     * Appends the given line of text to the end of the document.
     *
     * @param line  the text to append.
     **/
    public void appendText(String line) {
        try {
            insertString(getLength(), line + "\n", null);
        } catch (BadLocationException e) {
            LOGGER.error("Bad location!? getLength=" + getLength(), e);
        }
    }

    /**
     * Cancels the automatical update feature of the <code>de.renew.guiprompt.ResponseDocument</code>.
     **/
    public void terminate() {
        _terminated = true;
        _responseReaderThread.interrupt();
    }

    /**
     * Revives the automatical update feature of the
     * <code>de.renew.guiprompt.ResponseDocument</code> if has been paused due to an inactive
     * output thread.
     * <p>
     * This method cannot revive the feature after {@link #terminate} has
     * been called.
     * </p>
     **/
    public void revive() {
        synchronized (_monitor) {
            _monitor.notifyAll();
        }
    }
}