/*
 * @(#)LoopThread.java      0.9.0 06/02/2000 - 23:55:23
 *
 * 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;


/**
 * For threads which loop endlessly (which is a common thing), this
 * class implements the pause, start, and stop routines since JDK 1.2
 * deprecated theirs.
 * <p>
 * This class delegates the Thread object, so that this class doesn't
 * have to emulate all those thread methods.  In this way, a LoopThread
 * should act just like a Thread.
 *
 * @author   Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
 * @since    June 4, 2000
 * @version  $Date: 2003/02/10 22:52:48 $
 */
public class LoopThread
{
    public static final long MILLI_IN_SECOND = 1000;


    /* thread state: request a pause */
    private boolean tryPause = false;
    
    /* thread state: request a stop */
    private boolean tryStop = false;
    
    /* thread state: is it paused? */
    private boolean isPaused = false;
    
    /* thread state: is it running? */
    private boolean isRunning = false;
    
    /* wait/notify object for paused thread */
    private Object pauseObject = new Object();
    
    /* delay time to sleep */
    private long sleepTime = 6 * MILLI_IN_SECOND;
    
    /* Thread which runs in a loop */
    private Thread thisThread;
    
    /* the runnable to be executed at each loop */
    private Runnable runnable;

    /**
     * The runnable class - keep it private so no one
     * can directly access it in a thread but us.
     */
    private class LoopRunnable implements Runnable
    {
        public void run()
        {
            isRunning = true;
            isPaused = false;
            do
            {
                // this slows things down, but we're
                // a low priority anyway, so it doesn't really matter,
                // besides, it makes the check more than safe.
                synchronized( pauseObject )
                {
                    if (tryPause)
                    {
                        isPaused = true;
                        try
                        {
                            pauseObject.wait();
                        }
                        catch (InterruptedException ie)
                        {
                            // stop execution
                            tryStop = true;
                        }
                        isPaused = false;
                        // after pausing, we should check if we've been
                        // stopped.
                        continue;
                    }
                }
                // sleep

                if (sleepTime > 0 && !tryStop)
                {
//System.out.println("[Sleeping for "+sleepTime+" milliseconds]");
                    try { Thread.sleep( sleepTime ); }
                    catch (InterruptedException ie) {}
                }
    
                // Run the runnable
                if (!tryStop)
                {
//System.out.println("[Running loop]");
                    runnable.run();
                }
            } while (!tryStop);
            isRunning = false;
        }
    }
    
    
    /**
     * Used in case the implementing class needs to postpone initialization
     * until after the LoopThread construction.
     */
    public LoopThread()
    {
        this.thisThread = new Thread( new LoopRunnable() );
    }
    
    /**
     *
     */
    public LoopThread( Runnable loop )
    {
        this.runnable = loop;
        this.thisThread = new Thread( new LoopRunnable() );
    }
    
    /**
     *
     */
    public LoopThread( Runnable loop, String threadName )
    {
        this.runnable = loop;
        this.thisThread = new Thread( new LoopRunnable() );
    }
    
    /**
     *
     */
    public LoopThread( Runnable loop, ThreadGroup group )
    {
        this.runnable = loop;
        this.thisThread = new Thread( group, new LoopRunnable() );
    }
    
    /**
     *
     */
    public LoopThread( Runnable loop, ThreadGroup group, String threadName )
    {
        this.runnable = loop;
        this.thisThread = new Thread( group, new LoopRunnable(), threadName );
    }

    
    /**
     * @see java.lang.Thread#getPriority()
     */
    public int getPriority()
    {
        return this.thisThread.getPriority();
    }

        
    /**
     *
     * @see java.lang.Thread#setPriority( int )
     */
    public void setPriority( int priority )
    {
        this.thisThread.setPriority( priority );
    }

    
    /**
     *
     * @see java.lang.Thread#getThreadGroup()
     */
    public ThreadGroup getThreadGroup()
    {
        return this.thisThread.getThreadGroup();
    }
    
    /**
     *
     * @see java.lang.Thread#isAlive()
     */
    public boolean isAlive()
    {
        return this.thisThread.isAlive();
    }

    
    /**
     *
     * @see java.lang.Thread#isDaemon()
     */
    public boolean isDaemon()
    {
        return this.thisThread.isDaemon();
    }
    
    
    /**
     *
     * @see java.lang.Thread#setDaemon( boolean )
     */
    public void setDaemon( boolean on )
    {
        this.thisThread.setDaemon( on );
    }
    
    /**
     *
     * @see java.lang.Thread#join()
     */
    public void join() throws InterruptedException
    {
        if (this.isRunning)
        {
            throw new InterruptedException("can't join - thread is running");
        }
        this.thisThread.join();
    }
    
    
    /**
     * Sets the runnable instance after construction.  It cannot be
     * changed once the thread is running.
     */
    public void setRunnable( Runnable run )
    {
        if (this.isRunning)
        {
            throw new IllegalStateException("can't change a running thread's "+
                "runnable" );
        }
        this.runnable = run;
    }
    

    
    /**
     *
     * @see java.lang.Thread#start()
     */
    public void start()
    {
        if (this.isRunning)
        {
            throw new IllegalStateException("can't start a running thread");
        }
        this.tryPause = false;
        this.tryStop = false;
        this.thisThread.start();
    }


    /**
     * A non-deprecated, nice-to-the-system, thread suspension method.
     *
     * @see java.lang.Thread#suspend()
     */
    public void suspend()
    {
        if (this.tryStop || !this.isRunning)
        {
            throw new IllegalStateException("Cannot pause a stopped thread");
        }
        this.tryPause = true;
    }

    /**
     *
     * @see java.lang.Thread#resume()
     */
    public void resume()
    {
        synchronized( this.pauseObject )
        {
            if (!this.isPaused) return;
            this.tryPause = false;
            this.pauseObject.notifyAll();
        }
    }
    
    /**
     *
     * @see java.lang.Thread#stop()
     */
    public void stop()
    {
        if (!this.isRunning)
        {
            return; // tried stopping a stopped thread!
        }
        this.tryStop = true;
        
        // In case the thread is paused, we should resume it to have it
        // quit naturally.
        resume();
    }

    /**
     *
     */
    public boolean isPaused()
    {
        return this.isPaused;
    }
    
    /**
     *
     */
    public boolean isRunning()
    {
        return this.isRunning;
    }
    
    /**
     * Retrieves the sleep time in seconds.
     */
    public int getSleepTime()
    {
        return (int)(this.sleepTime / MILLI_IN_SECOND);
    }
    
    /**
     * Sets the sleep time in seconds.
     */
    public void setSleepTime( int seconds )
    {
        this.sleepTime = seconds * MILLI_IN_SECOND;
    }
    
    /**
     * Sets the sleep time in milliseconds.
     */
    public void setSleepTimeMillis( long millis )
    {
        this.sleepTime = millis;
    }
    
    
    /**
     * Retrieves the sleep time in milliseconds.
     */
    public long getSleepTimeMillis()
    {
        return this.sleepTime;
    }
     
     
    
    /**
     *
     * @see java.lang.Thread#toString()
     */
    public String toString()
    {
        return this.thisThread.toString();
    }
}
