/*
 * BasicThreadPool.java  $Revision: 1.4 $  $Date: 2007/10/03 04:39:39 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Scaffold is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.meldcraft.util;

import java.util.ArrayList;

/**
 * A basic thread pool implementation.
 *
 * @version $Revision: 1.4 $ $Date: 2007/10/03 04:39:39 $
 * @author rob
 */
public class BasicThreadPool
{
// Static variables
// Member variables
    private int         mMaxThreads;
    private boolean     mCoalesce;
    private ArrayList   mTaskList = new ArrayList();
    private ArrayList   mThreads = new ArrayList();

// Constructors and initialization
    public BasicThreadPool(int maxThreads, String threadNamePrefix)
    {
        this(maxThreads, threadNamePrefix, false);
    }

    public BasicThreadPool(int maxThreads, String threadNamePrefix, boolean coalesce)
    {
        mCoalesce = coalesce;
        mMaxThreads = maxThreads;
        for (int i=0 ; i < mMaxThreads ; i++)
        {
            Thread thread = new ThreadPoolTask(this, threadNamePrefix);
            mThreads.add(thread);
            thread.start();
        }
    }

// Interface implemenations
// Public methods
    /**
     * Stops all of the threads in the pool.
     */
    public void stop()
    {
        ThreadPoolTask[] threads = (ThreadPoolTask[])mThreads.toArray(
                                                        new ThreadPoolTask[0]);
        mThreads.clear();

        for (int i=0 ; i < threads.length ; i++)
        {
            ThreadPoolTask thread = threads[i];
            thread.stopRunning();
        }
    }

    /**
     * Adds a task to perform.  Tasks are passed to the first available thread
     * in the order in which they are recieved (i.e. FIFO).
     */
    public synchronized void addTask(Runnable run)
    {
        if (!mTaskList.contains(run))
        {
            mTaskList.add(run);
            notify();
        }
    }

// Protected methods
// Private methods
    private Runnable nextTask()
    {
        if (mTaskList.size() <= 0)
        {
            return null;
        }
        Runnable ret;
        if (mCoalesce)
        {
            ret = (Runnable)mTaskList.get(mTaskList.size()-1);
            mTaskList.clear();
        }
        else
        {
            ret = (Runnable)mTaskList.remove(0);
        }
        return ret;
    }

// Inner classes
    private static class ThreadPoolTask extends Thread
    {
        private static int          sInstanceCount = 0;
        private BasicThreadPool     mPool;
        private boolean             mRunning;

        private ThreadPoolTask(BasicThreadPool pool, String namePrefix)
        {
            super(namePrefix + "-" + getNextInstanceCount());
            mPool = pool;
        }

        public void run()
        {
            setRunning(true);

            while (isRunning())
            {
                try
                {
                    Runnable run = null;
                    synchronized(mPool)
                    {
                        run = mPool.nextTask();
                        if (run == null)
                        {
                            //System.err.println("wait");
                            mPool.wait();
                        }
                    }
                    if (run != null)
                    {
                        run.run();
                    }
                }
                catch (InterruptedException e)
                {
                    // ignored
                }
                catch (Throwable t)
                {
                    t.printStackTrace();
                }
            }
        }

        public boolean isRunning()
        {
            synchronized(this)
            {
                return mRunning;
            }
        }

        public void stopRunning()
        {
            setRunning(false);
            interrupt();
        }

        protected void setRunning(boolean running)
        {
            synchronized(this)
            {
                mRunning = running;
            }
        }

        protected static synchronized int getNextInstanceCount()
        {
            return sInstanceCount++;
        }
    }
}
