/*
 * @(#)IOThreadRunner.java    1.0.0 11/17/2000 - 13:41:07
 *
 * Copyright (C) 2000,,2003 2002 Matt Albrecht
 * groboclown@users.sourceforge.net
 * http://groboutils.sourceforge.net
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the 
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in 
 *  all copies or substantial portions of the Software. 
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL 
 *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE.
 */

package net.sourceforge.groboutils.util.thread.v1;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


/**
 * Loops, reading from the input stream and writes to the output stream.
 *
 *
 * @author   Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @since    November 17, 2000
 * @version  $Date: 2003/02/10 22:52:48 $
 * @see      LoopThread
 */
public class IOThreadRunner
{
    private static final int BUFFER_SIZE = 4096;
    private InputStream is;
    private OutputStream os;
    private byte[] buffer = new byte[ BUFFER_SIZE ];
    private LoopThread lt;
    private IOException exception;
    private boolean closeInputOnEOF = false;
    private boolean closeOutputOnEOF = false;
    
    private Runnable ltRunner = new Runnable()
        {
            public void run()
            {
                try
                {
                    int read = is.read( buffer, 0, BUFFER_SIZE );
                    if (read > 0)
                    {
/*
System.out.print("[IOThreadRunner: read: {");
System.out.write( buffer, 0, read );
System.out.println("} ]");
*/
                        os.write( buffer, 0, read );
                    }
                    else
                    {
//System.out.print("[IOThreadRunner: EOF]");
                        reachedEOF();
                    }
                }
                catch (IOException e)
                {
                    registerException( e );
                }
            }
        };
    
    /**
     * Create a new ThreadRunner, re-routing <tt>is</tt> data into the
     * <tt>os</tt> stream.
     */
    public IOThreadRunner( InputStream is, OutputStream os )
    {
        this( new LoopThread(), is, os );
        
        this.lt.setDaemon( true );
        this.lt.setSleepTimeMillis( 0L );
    }
    
    
    /**
     * Create a new ThreadRunner, re-routing <tt>is</tt> data into the
     * <tt>os</tt> stream, but uses the initialization of the given
     * LoopThread.  Note that <tt>lt</tt> must not be a running thread.
     */
    public IOThreadRunner( LoopThread lt, InputStream is, OutputStream os )
    {
        this.lt = lt;
        lt.setRunnable( ltRunner );
        this.is = is;
        this.os = os;
    }
    
    
    /**
     * Retrieves the most recent exception that occured, if any.  If no
     * exception happened, then it returns <tt>null</tt>.
     */
    public IOException getException()
    {
        return this.exception;
    }
    
    
    /**
     * By setting this to <tt>true</tt> the runner will close the InputStream
     * once a Stop signal has been encountered.
     */
    public void setCloseInputOnStop( boolean on )
    {
        this.closeInputOnEOF = on;
    }
    
    
    /**
     * By setting this to <tt>true</tt> the runner will close the OutputStream
     * once a Stop signal has been encountered.
     */
    public void setCloseOutputOnStop( boolean on )
    {
        this.closeOutputOnEOF = on;
    }
    
    
    /**
     * Retrieves the LoopThread instance that manages the operation. You
     * can use this instance for setting up the thread (such as setting
     * priority and daemon status).
     */
    public LoopThread getThread()
    {
        return this.lt;
    }
    
    
    /**
     * Retrieves the output stream that the input is redirected to.
     */
    public OutputStream getOutputStream()
    {
        return this.os;
    }
    
    
    /**
     * @return <tt>true</tt> if the thread is alive and reading the
     *      stream, or <tt>false</tt> if it is not reading.
     */
    public boolean isReading()
    {
        return this.lt.isAlive();
    }
    
    
    /**
     * Starts the stream reading.
     */
    public void start()
    {
        this.lt.start();
    }
    
    
    /**
     * Stops the thread from reading.
     */
    public void stop()
            throws IOException
    {
        if (this.closeInputOnEOF)
        {
            this.is.close();
        }
        if (this.closeOutputOnEOF)
        {
            this.os.close();
        }
        this.lt.stop();
    }
    
    
    /**
     * Post an exception, and stop the reading.
     */
    protected void registerException( IOException ioe )
    {
        this.exception = ioe;
        try
        {
            stop();
        }
        catch (IOException ex)
        {
            // ignore
        }
    }
    
    
    /**
     * Stop the reading and void out any exceptions.
     */
    protected void reachedEOF()
    {
        this.exception = null;
        try
        {
            stop();
        }
        catch (IOException ioe)
        {
            this.exception = ioe;
        }
    }
}
