package org.itscool.commons.pool;

import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;

/**
 * オブジェクトをプールするクラスです
 * @author KANO
 * 作成日： 2004/12/07
 */
public class ObjectPool{
    private ObjectPoolEventListener listener = null;
    private Vector pool = new Vector();
    private int initSize;        //プールの初期値
    private int maxSize;        //プールの最大値
    
    /**
     * プールの初期化<br>
     * プールの最大サイズが初期サイズより小さい場合はPoolRuntimeExceptionをスローします
     * @param listener プールからイベントの発行先となるオブジェクトを指定します
     * @param initSize プールの初期サイズ
     * @param maxSize プールのMAXサイズ
     */
    public ObjectPool(ObjectPoolEventListener listener,
        int initSize, int maxSize ){
        this.listener = listener;
        this.initSize = initSize;
        this.maxSize = maxSize;
        if( maxSize < initSize ){
            throw new ObjectPoolRuntimeException("pool size is warning(maxSize < initSize).");
        }
    }
    
    /**
     * 現在のプールサイズを返します
     * @return 現在のプールサイズを返します
     */
    public int getPoolSize(){
        return pool.size();
    }
    
    /**
     * プールの初期サイズを返します
     * @return プールの初期サイズを返します
     */
    public int getInitSize(){
        return initSize;
    }
    
    /**
     * プールの拡張可能な最大サイズを返します
     * @return 拡張可能なプールサイズを返します
     */
    public int getMaxSize(){
        return maxSize;
    }
    
    /**
     * プール情報を返します
     * @return プール情報を返します
     */
    public String toString(){
        StringBuffer sb = new StringBuffer();
        sb.append("[INIT SIZE]").append(initSize).append(", ");
        sb.append("[POOL SIZE]").append(pool.size()).append(", ");
        sb.append("[MAX  SIZE]").append(maxSize).append(", ");
        
        return sb.toString();
    }
    
    /**
     * 要素を新たに追加します<br>
     * プールサイズがMAXサイズと同じかそれ以上の場合は
     * PoolRuntimeExceptionをスローします。<br>
     * プールサイズを拡張した後はnotifyAll()を発行し、次の要素の取得待ちを
     * しているオブジェクトを起こします。
     * @param obj 要素
     */
    public synchronized ObjectPoolElement addNewElement(Object obj) {
        if( pool.size() >= maxSize ){
            throw new ObjectPoolRuntimeException("pool size is MAX");
        }
        ObjectPoolElement element = new ObjectPoolElement();
        element.setObject(obj);
        element.setUse(false);
        pool.addElement(element);
        notifyAll();
        return element;
    }
    
//    private ObjectPoolElement createElement(Object obj){
//        ObjectPoolElement element = new ObjectPoolElement();
//        element.setObject(obj);
//        element.setUse(false);
//        return element;
//    }
//
    /**
     * 使用済みの要素をプールに返す<br>
     * 使用済みのプールを返却した後はnotifyAll()を発行し、次の要素の取得待ちを
     * しているオブジェクトを起こします。
     * @param obj 要素
     */
    public synchronized void returnElement(Object obj){
        for(int i=0; i<pool.size(); i++ ){
            ObjectPoolElement elem = (ObjectPoolElement)pool.get(i);
            if(elem.getObject() == obj){
                elem.setUse(false);
            }
        }
        //pool.addElement(obj);
        notifyAll();
    }
    
    /**
     * プールから要素を取得<BR>
     * 要素が不足している場合はdoMissingElementEvent()を発行し
     * プールサイズの拡張とプールに新しい要素の追加を要求します<br>
     * プールサイズがMAXサイズに達している場合はプールサイズの拡張
     * は行われず、null要素を返します。
     *
     * @return 要素を返します
     */
    public synchronized Object getElement() {
        Object obj = null;
        for(int i=0; i<pool.size(); i++ ){
            ObjectPoolElement elem = (ObjectPoolElement)pool.get(i);
            if( !elem.isUse() ){
                elem.setUse(true);
                return elem.getObject();
            }
        }
        
        //MAXサイズが指定されていない場合や
        //現在のプールサイズがMAXサイズより小さい場合は
        //プールの追加要求イベントを発行する
        if(pool.size() < maxSize){
            obj = listener.doMissingElementEvent();
            if( obj != null ){
                ObjectPoolElement elem = addNewElement(obj);
                elem.setUse(true);
                return elem.getObject();
            }
        }
        //return null;
        throw new ObjectPoolRuntimeException("pool is no buffer.");
//        if (pool.size() > 0) {
//            obj = pool.firstElement();
//            pool.removeElementAt(0);
//            restSize--;
//        //MAXサイズが指定されていない場合や
//        //現在のプールサイズがMAXサイズより小さい場合は
//        //プールの追加要求イベントを発行する
//        }else if(poolSize < maxSize){
//            obj = listener.doMissingElementEvent();
//            if( obj != null ){
//                poolSize++;
//            }
//        }
//        return obj;
    }
    
    /**
     * プールから要素を取得<BR>
     * 要素が不足している場合はtimeout秒で指定された時間だけ
     * 待ちます<br>
     * その後、再度要素の取得を試みます<BR>
     * 要素の取得に失敗した場合はnullを返します
     * @return 要素を返します
     */
    public synchronized Object getElement(int timeout) {
        long startTime = new Date().getTime();
        Object obj = null;
        while ((obj = getElement()) == null) {
            try {
                wait(timeout);
            }catch (InterruptedException e) {}
            if ((new Date().getTime() - startTime) >= timeout) {
                // Timeout has expired
                //return null;
                throw new ObjectPoolRuntimeException("pool is no buffer.");
            }
        }
        return obj;
    }
    /**
     * 全ての要素を解放します
     */
    public synchronized void release() {
        Enumeration element = pool.elements();
        while (element.hasMoreElements()) {
            ObjectPoolElement elem = (ObjectPoolElement)element.nextElement();
            listener.doReleaseElementEvent(elem.getObject());
        }
        pool.removeAllElements();
    }
    
}

