package org.itscool.commons.threadpool;

/**
 * This class implements the Thread Pool.
 * @author Ashwin Desai.
 * @version 1.10A 2002.06.23 加納 充照 キューのバッファサイズの指定を追加
 */
public class ThreadPool {
    /** Default Number of Threads in the pool */
    public static final int DEFAULT_MAX_THREADS = 10;
    /** Max Number of Threads in the pool. */
    private int maxThreads;
    /** Threads array. */
    private WorkerThread[] threadPool;
    /** Request Buffer.*/
    private FIFOQueue workQueue;

    /**
     * @link
     * @shapeType PatternLink
     * @pattern TestCase
     * @clientRole tested
     * @supplierRole tests
     * @hidden 
     */
    /*# private TestThreadPool _testThreadPool; */

    /**
     * Creates a new ThreadPool with 'maxthreadcount' number of threads.
     * スレッドの「maxthreadcount」ナンバーで新しいThreadPoolを作り出します。
     * @param maxthreadcount スレッド数を指定します
     */
    public ThreadPool(int maxthreadcount) {
        this.maxThreads = maxthreadcount;
        threadPool = new WorkerThread[maxThreads];
        workQueue = new FIFOQueue();
        initThreads();
    }

    /**
    * スレッドプールの生成 <BR>
    * キューのサイズとバッファのサイズを指定する。<BR>
    * サイズの指定は【 キューのサイズ ＜ バッファのサイズ】となるように
    * 指定することを推奨する。<BR>
    *   (キューに追加されるスレッド数が、バッファサイズを超えるとThread
    * BufferExceptionをスローするため）。
    * @param maxthreadcount : キューのサイズ
    * @param maxbuffersize : バッファのサイズ
    */
    public ThreadPool(int maxthreadcount, int maxbuffersize) {
        this.maxThreads = maxthreadcount;
        threadPool = new WorkerThread[maxThreads];
        workQueue = new FIFOQueue(maxbuffersize);
        initThreads();
    }

    /**
     * Creates a new ThreadPool with 2 threads (default).
     * DEFAULT_MAX_THREADSのスレッドとDEFAULT_BUFFER_SIZEのリクエスト
     * バッファをもつ新しいThreadPoolを作り出します(デフォルト)。
     */
    public ThreadPool() {
        this(DEFAULT_MAX_THREADS, FIFOQueue.DEFAULT_BUFFER_SIZE);
    }

    /**
     * Creates a new WorkerThread for each thread in the array.
     * 配列にそれぞれのスレッドのための新しいWorkerThreadを作り出します。
     */
    private void initThreads() {
        for (int i = 0; i < threadPool.length; i++) {
            threadPool[i] = new WorkerThread(workQueue);
        }
    }

    /**
     * Adds a new Request to the Queue.
     * キューに新しいリクエストを加えます。
     * @param target スレッドを指定します
     * @throws ClosedException if the request queue is already closed.
     * もしリクエスト・キューがすでに閉じられればCloseExceptionを投げる。
     */
    public void addRequest(Runnable target)
        throws ClosedException, ThreadBufferException {
        try {
            workQueue.add(target);
        } catch (ClosedException ce) {
            throw ce;
        } catch (ThreadBufferException tbe) {
            throw tbe;
        }
    }

    /**
     * Closes the Thread Pool. This method does the following.
     * <br>1) Stops the worker threads by calling setStopped() on each workerThread.
     * <br>2) Closes the request buffer.
     * スレッド・プールを閉じます。このメソッドは以下を処理します。
     * １)労働者がそれぞれのworkerThreadでsetStopped ()を呼ぶことによって糸を通すストップ。
     * ２)リクエスト・バッファーを閉じます。
     */
    public void close() {
        stopThreads();
        workQueue.close();
    }

    /**
     * Closes the Thread Pool. This method does the following.
     * <br>1) Stops the worker threads by calling setStopped() on each workerThread.
     * <br>2) Closes the request buffer.
     * スレッド・プールを閉じます。このメソッドは以下を処理します。
     * １)労働者がそれぞれのworkerThreadでsetStopped ()を呼ぶことによって糸を通すストップ。
     * ２)リクエスト・バッファーを閉じます。
     * @param waitTime 待ち時間を指定します
     * @return waitCountを返します
     */
    public int close(int waitTime) {
        this.close();

        int sleepTime = 1000;
        int waitCount = 0;
        int i = 0;
        for (; sleepTime * i < waitTime; i++) {
            if (workQueue.requestEmpty()){
                break;
            }
            
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException interruptedexception) {
                /* empty */
            }
        }

        for (; sleepTime * i < waitTime; i++) {
            for (waitCount = 0; waitCount < threadPool.length; waitCount++) {
                if (threadPool[waitCount].getWaitStatus()
                    == WorkerThread.AN_WAIT){
                    break;
                }
            }
            if (waitCount >= threadPool.length){
                break;
            }
            
            try {
                Thread.sleep(sleepTime);
            } catch (InterruptedException interruptedexception) {
                /* empty */
            }
            //            System.out.println("close Wait waitCount = " + waitCount);
        }
        return waitCount;
    }

    /**
     * Stops the worker threads by calling setStopped() on each workerThread.
     * それぞれのworkerThreadでsetStopped ()を呼ぶことによってワーカースレッドを
     * 停止する。
     */
    private void stopThreads() {
        for (int i = 0; i < threadPool.length; i++) {
            threadPool[i].setStopped();
        }
    }

    /**
     * For Debug info.
     */
    public void printStats() {
        System.out.println("Thread Usage ");
        for (int i = 0; i < threadPool.length; i++) {
            System.out.println(threadPool[i].toString());
        }
    }
}
