/*
 * Scone - The Web Enhancement Framework
 * Copyright (C) 2009 Harald Weinreich, Volkert Buchmann, Frank Wollenweber, Torsten Haß
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
 package scone.robot;


import java.net.URL;
import java.util.Enumeration;
import java.util.Vector;

import HTTPClient.HTTPConnection;
import HTTPClient.ParseException;
import HTTPClient.URI;


/**
 * A pool of http connections used for efficiency reasons
 *
 * @author Frank Wollenweber
 */



class HttpConnectionPool {

    private Vector connections; // The vector with the connections
    private int maxConnections; // Only maxConnections Connectios are allowd to be in the pool

    /**
     * The constructor
     * @param maxConnections Only maxConnections connections are allowed to stay in the connection-pool
     */
    HttpConnectionPool(int maxConnections) {
        connections = new Vector();
        this.maxConnections = maxConnections;
    }

    /**
     * Get an existing http connection compatible to url or a new connection
     * @param url The url to open a new connection to.
     * @return an existing http connection or a new one, if there isn't an existing connection
     */
    synchronized RobotHttpConnection getHttpConnection(URL url) {
        URI uri = null;

        try {
            uri = new URI(url);
        } catch (ParseException ex) {
            System.out.println("HttpConnectionPool: invalid url");
        }
        Enumeration connection = connections.elements();
        PoolHttpConnection con = null;

        // Check, if there's an unused connection to the url
        while (connection.hasMoreElements()) {
            con = (PoolHttpConnection) connection.nextElement();
            if ((!con.isUsed()) && (con.isCompatibleWith(uri))) {
                con.setUsed();
                return con;
            }
        }

        // No connection found
        if (connections.size() > maxConnections) {
            removeUnusedConnections();
        }
        String host = url.getHost();
        int port = url.getPort();

        con = new PoolHttpConnection(host, port);
        connections.add(con);
        con.setUsed();
        return con;
    }

    /**
     * Removes unused connections from the pool *
     **/
    private synchronized void removeUnusedConnections() {
        Enumeration connection = connections.elements();
        PoolHttpConnection con;

        while (connection.hasMoreElements()) {
            con = (PoolHttpConnection) connection.nextElement();
            if (!con.isUsed()) {
                con.stop();
                connections.removeElement(con);
            }
        }
    }

    /**
     * After using this connection this method should be called to mark the connection as unused.
     * @param con Mark this HTTPConnection as unused
     **/
    synchronized void returnHttpConnection(HTTPConnection con) {
        ((PoolHttpConnection) con).setUnused();
    }

    /**
     * A HttpConnection with a flag, which indicates íf the connection is used
     **/

    private class PoolHttpConnection extends RobotHttpConnection {

        private boolean used; // True, if the connection is currently used

        /**
         * constructor
         * @param host Build up a connection to this host
         * @param port Build up a connection to this port
         **/
        PoolHttpConnection(String host, int port) {
            super(host, port);
            used = false;
            uses = 0;
        }

        /**
         * Marks this connection as used
         **/
        synchronized void setUsed() {
            used = true;
            if (uses < 2) {
                uses++;
            }
        }

        /**
         * Marks this connection as unused
         **/
        synchronized void setUnused() {
            used = false;
        }

        /**
         * Checks if this connection is used
         * @return true, if the connection is currently used
         **/
        synchronized boolean isUsed() {
            return used;
        }
    }
}
