/*
 * Distributed as part of c3p0 v.0.9.5.2
 *
 * Copyright (C) 2015 Machinery For Change, Inc.
 *
 * Author: Steve Waldman <swaldman@mchange.com>
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of EITHER:
 *
 *     1) The GNU Lesser General Public License (LGPL), version 2.1, as 
 *        published by the Free Software Foundation
 *
 * OR
 *
 *     2) The Eclipse Public License (EPL), version 1.0
 *
 * You may choose which license to accept if you wish to redistribute
 * or modify this work. You may offer derivatives of this work
 * under the license you have chosen, or you may provide the same
 * choice of license which you have been offered here.
 *
 * This software 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.
 *
 * You should have received copies of both LGPL v2.1 and EPL v1.0
 * along with this software; see the files LICENSE-EPL and LICENSE-LGPL.
 * If not, the text of these licenses are currently available at
 *
 * LGPL v2.1: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
 *  EPL v1.0: http://www.eclipse.org/org/documents/epl-v10.php 
 * 
 */

package com.mchange.v2.resourcepool;

import java.util.*;
import com.mchange.v2.async.*;

public class BasicResourcePoolFactory extends ResourcePoolFactory
{
    public static BasicResourcePoolFactory createNoEventSupportInstance( int num_task_threads )
    { return createNoEventSupportInstance( null, null, num_task_threads ); }

    public static BasicResourcePoolFactory createNoEventSupportInstance( AsynchronousRunner taskRunner, 
									 Timer timer )
    { return createNoEventSupportInstance( taskRunner, timer, ResourcePoolFactory.DEFAULT_NUM_TASK_THREADS ); }


    private static BasicResourcePoolFactory createNoEventSupportInstance( AsynchronousRunner taskRunner, 
									  Timer timer,
									  int default_num_task_threads )
    {
	return new BasicResourcePoolFactory( taskRunner, 
					     timer,
					     default_num_task_threads,
					     true );
    }

    int     start                         = -1;   //default to min
    int     min                           = 1;
    int     max                           = 12;
    int     inc                           = 3;
    int     retry_attempts                = -1;   //by default, retry acquisitions forever
    int     retry_delay                   = 1000; //1 second
    long    idle_resource_test_period     = -1;   //milliseconds, by default we don't test idle resources
    long    max_age                       = -1;   //milliseconds, by default resources never expire
    long    max_idle_time                 = -1;   //milliseconds, by default resources never expire
    long    excess_max_idle_time          = -1;   //milliseconds, by default resources never expire
    long    destroy_overdue_resc_time     = -1;   //milliseconds
    long    expiration_enforcement_delay  = -1;   //automatic, we come up with a reasonable default based on time params

    boolean break_on_acquisition_failure    = true;
    boolean debug_store_checkout_stacktrace = false;
    boolean force_synchronous_checkins      = false;

    AsynchronousRunner taskRunner;
    boolean            taskRunner_is_external;

    RunnableQueue asyncEventQueue;
    boolean       asyncEventQueue_is_external;

    Timer   timer;
    boolean timer_is_external;

    int default_num_task_threads;

    Set liveChildren;



    //OLD
//      Set rqUsers = null;
//      SimpleRunnableQueue rq = null;

//      Set timerUsers = null;
//      Timer timer = null;
    //END OLD

    BasicResourcePoolFactory()
    { this( null, null, null ); }

    BasicResourcePoolFactory( AsynchronousRunner taskRunner, 
			      RunnableQueue asyncEventQueue,  
			      Timer timer )
    { this ( taskRunner, asyncEventQueue, timer, DEFAULT_NUM_TASK_THREADS ); }

    BasicResourcePoolFactory( int num_task_threads )
    { this ( null, null, null, num_task_threads ); }

    BasicResourcePoolFactory( AsynchronousRunner taskRunner, 
			      Timer timer,
			      int default_num_task_threads,
			      boolean no_event_support)
    { 
	this( taskRunner, null,  timer, default_num_task_threads );
	if (no_event_support)
	    asyncEventQueue_is_external = true; //if it's null, and external, it simply won't exist...
    }

    BasicResourcePoolFactory( AsynchronousRunner taskRunner, 
			      RunnableQueue asyncEventQueue,  
			      Timer timer,
			      int default_num_task_threads)
    {  
	this.taskRunner = taskRunner;
	this.taskRunner_is_external = ( taskRunner != null );

	this.asyncEventQueue = asyncEventQueue;
	this.asyncEventQueue_is_external = ( asyncEventQueue != null );

	this.timer = timer;
	this.timer_is_external = ( timer != null );

	this.default_num_task_threads = default_num_task_threads;
    }

    private void createThreadResources()
    {
	if (! taskRunner_is_external )
	    taskRunner = new ThreadPoolAsynchronousRunner( default_num_task_threads, true );
	if (! asyncEventQueue_is_external)
	    asyncEventQueue = new CarefulRunnableQueue( true, false );
	if (! timer_is_external )
	    timer = new Timer( true );

	this.liveChildren = new HashSet();
    }

    private void destroyThreadResources()
    {
	if (! taskRunner_is_external )
	    {
		taskRunner.close();
		taskRunner = null;
	    }
	if (! asyncEventQueue_is_external )
	    {
		asyncEventQueue.close();
		asyncEventQueue = null;
	    }
	if (! timer_is_external )
	    {
		timer.cancel();
		timer = null;
	    }

	this.liveChildren = null;
    }

//      synchronized RunnableQueue getSharedRunnableQueue( BasicResourcePool pool )
//      {
//  	if (rqUsers == null)
//  	    {
//  		rqUsers = new HashSet();
//  		rq = new SimpleRunnableQueue(true);
//  	    }
//  	rqUsers.add( pool );
//  	return rq;
//      }
    
//      synchronized Timer getTimer( BasicResourcePool pool )
//      {
//  	if (timerUsers == null)
//  	    {
//  		timerUsers = new HashSet();
//  		timer = new Timer( true );
//  	    }
//  	timerUsers.add( pool );
//  	return timer;
//      }

    synchronized void markBroken( BasicResourcePool pool )
    {
	//System.err.println("markBroken -- liveChildren: " + liveChildren);
	if (liveChildren != null) //keep this method idempotent!
	    {
		liveChildren.remove( pool );
		if (liveChildren.isEmpty())
		    destroyThreadResources();
	    }
//  	rqUsers.remove( pool );
//  	if (rqUsers.size() == 0)
//  	    {
//  		rqUsers = null;
//  		rq.close();
//  		rq = null;
//  	    }

//  	timerUsers.remove( pool );
//  	if (timerUsers.size() == 0)
//  	    {
//  		timerUsers = null;
//  		timer.cancel();
//  		timer = null;
//  	    }
    }
    
    /**
     * If start is less than min, it will
     * be ignored, and the pool will start
     * with min.
     */
    public synchronized void setStart( int start )
	throws ResourcePoolException
    { this.start = start; }

    public synchronized int getStart()
	throws ResourcePoolException
    { return start; } 

    public synchronized void setMin( int min )
	throws ResourcePoolException
    { this.min = min; }

    public synchronized int getMin()
	throws ResourcePoolException
    { return min; }

    public synchronized void setMax( int max )
	throws ResourcePoolException
    { this.max = max; }

    public synchronized int getMax()
	throws ResourcePoolException
    { return max; }

    public synchronized void setIncrement( int inc )
	throws ResourcePoolException
    { this.inc = inc; }

    public synchronized int getIncrement()
	throws ResourcePoolException
    { return inc; }

    public synchronized void setAcquisitionRetryAttempts( int retry_attempts )
	throws ResourcePoolException
    { this.retry_attempts = retry_attempts; }

    public synchronized int getAcquisitionRetryAttempts()
	throws ResourcePoolException
    { return retry_attempts; }

    public synchronized void setAcquisitionRetryDelay( int retry_delay )
	throws ResourcePoolException
    { this.retry_delay = retry_delay; }

    public synchronized int getAcquisitionRetryDelay()
	throws ResourcePoolException
    { return retry_delay; }

    public synchronized void setIdleResourceTestPeriod( long test_period )
    { this.idle_resource_test_period = test_period; }

    public synchronized long getIdleResourceTestPeriod()
    { return idle_resource_test_period; }

    public synchronized void setResourceMaxAge( long max_age )
	throws ResourcePoolException
    { this.max_age = max_age; }

    public synchronized long getResourceMaxAge()
	throws ResourcePoolException
    { return max_age; }

    public synchronized void setResourceMaxIdleTime( long millis )
	throws ResourcePoolException
    { this.max_idle_time = millis; }

    public synchronized long getResourceMaxIdleTime()
	throws ResourcePoolException
    { return max_idle_time; }

    public synchronized void setExcessResourceMaxIdleTime( long millis )
	throws ResourcePoolException
    { this.excess_max_idle_time = millis; }

    public synchronized long getExcessResourceMaxIdleTime()
	throws ResourcePoolException
    { return excess_max_idle_time; }

    public synchronized long getDestroyOverdueResourceTime()
	throws ResourcePoolException
    { return destroy_overdue_resc_time; }

    public synchronized void setDestroyOverdueResourceTime( long millis )
	throws ResourcePoolException
    { this.destroy_overdue_resc_time = millis; }

    public synchronized void setExpirationEnforcementDelay( long expiration_enforcement_delay )
	throws ResourcePoolException
    { this.expiration_enforcement_delay = expiration_enforcement_delay; }

    public synchronized long getExpirationEnforcementDelay()
	throws ResourcePoolException
    { return expiration_enforcement_delay; }

    public synchronized void setBreakOnAcquisitionFailure( boolean break_on_acquisition_failure )
	throws ResourcePoolException
    { this.break_on_acquisition_failure = break_on_acquisition_failure; }

    public synchronized boolean getBreakOnAcquisitionFailure()
	throws ResourcePoolException
    { return break_on_acquisition_failure; }

    public synchronized void setDebugStoreCheckoutStackTrace( boolean debug_store_checkout_stacktrace )
	throws ResourcePoolException
    { this.debug_store_checkout_stacktrace = debug_store_checkout_stacktrace; }

    public synchronized boolean getDebugStoreCheckoutStackTrace()
	throws ResourcePoolException
    { return debug_store_checkout_stacktrace; }

    public synchronized void setForceSynchronousCheckins( boolean force_synchronous_checkins )
	throws ResourcePoolException
    { this.force_synchronous_checkins = force_synchronous_checkins; }

    public synchronized boolean getForceSynchronousCheckins()
	throws ResourcePoolException
    { return force_synchronous_checkins; }

    public synchronized ResourcePool createPool(ResourcePool.Manager mgr)
	throws ResourcePoolException
    {
	if (liveChildren == null)
	    createThreadResources();
	//System.err.println("Created liveChildren: " + liveChildren);
	ResourcePool child = new BasicResourcePool( mgr, 
						    start, 
						    min, 
						    max, 
						    inc, 
						    retry_attempts, 
						    retry_delay, 
						    idle_resource_test_period,
						    max_age, 
						    max_idle_time,
						    excess_max_idle_time,
						    destroy_overdue_resc_time,
						    expiration_enforcement_delay,
						    break_on_acquisition_failure,
						    debug_store_checkout_stacktrace,
						    force_synchronous_checkins,
						    taskRunner,
						    asyncEventQueue,
						    timer,
						    this );
	liveChildren.add( child );
	return child;
    }
}







