/*
 *    Geotools2 - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002, Geotools Project Managment Committee (PMC)
 *
 *    This library 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;
 *    version 2.1 of the License.
 *
 *    This library 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.
 *
 */
 package org.geotools.data.geometryless.wrapper;

import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.logging.Logger;

//import javax.sql.ConnectionPoolDataSource;

/**
 * <p>
 * Wraps a Postgres ConnectionPoolDataSource implementation to support the setURL()
 * method
 * </p>
 * <p>
 * The jdbc3 8.0x driver includes org.postgresql.jdbc2.optional.ConnectionPool,
 * which wrappers org.postgresql.ds.PGConnectionPool, so you could use that as
 * the extension class for 8.0x driver, ie instead of
 * org.postgresql.jdbc2.optional.ConnectionPool, but it does't really matter,
 * however that's why this class has the name it has
 * </p>
 * <p>
 * in info.xml, you need to set <parameter name="driver" value=
 * "org.geotools.data.geometryless.wrapper.PGConnectionPool"/>
 * </p>
 * 
 * @author Rob Atkinson rob@socialchange.net.NOSPAM.au
 * @author Peter Barrs@socialchange.net.NOSPAM.au
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/geometryless/src/main/java/org/geotools/data/geometryless/wrapper/PGConnectionPool.java $
 */
public class PGConnectionPool extends org.postgresql.jdbc2.optional.ConnectionPool { 
	


	/**
	 * 
	 */
	private static final long serialVersionUID = -1157879368880510850L;

	/** Standard logging instance */
	private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.geometryless");

	//private ConnectionPoolDataSource _nativePool;

	/** Creates configuration-driven JDBC driver class. */

	private String _dbURL;

	//private String _username = "";

	//private String _password = "";

	/**
	 * Sets the JDBC database login credentials.
	 * 
	 * @param complete
	 *            JDBC connectionURL
	 */
	public void setURL(String dbURL) throws SQLException {

		_dbURL = dbURL;
		try {
			// _nativePool.getClass().getMethod("setURL", new Class [] {
			// String.class } ).invoke( _nativePool, new Object [] { _dbURL } );
			Properties props = parseURL(_dbURL, new Properties());

			LOGGER.finer("setting ServerName = " + props.getProperty("ServerName"));
			setServerName(props.getProperty("ServerName"));
			
			if (props.getProperty("PortNumber") != null || !props.getProperty("PortNumber").equals("")) {
				LOGGER.finer("setting ServerName = " + props.getProperty("PortNumber"));
				setPortNumber(Integer.parseInt(props.getProperty("PortNumber")));
			}
			
			LOGGER.finer("setting DatabaseName = " + props.getProperty("DatabaseName"));
			setDatabaseName(props.getProperty("DatabaseName"));

		} catch (Exception e) {
			LOGGER.severe("Failed to instantiate connection pool using " + _dbURL);
			throw new SQLException(
					"Failed to instantiate connection pool using " + _dbURL
							+ "(" + e + ")");
		}
	}

	/**
	 * Parse the driver URL and extract the properties.
	 * 
	 * @param url
	 *            The URL to parse.
	 * @param info
	 *            Any existing properties already loaded in a Properties object.
	 * @return The URL properties as a <code>Properties</code> object.
	 */
	private static Properties parseURL(String url, Properties info) {
		Properties props = new Properties();

		// Take local copy of existing properties
		for (Enumeration e = info.keys(); e.hasMoreElements();) {
			String key = (String) e.nextElement();
			String value = info.getProperty(key);

			if (value != null) {
				props.setProperty(key.toUpperCase(), value);
			}
		}

		StringBuffer token = new StringBuffer(128);
		int pos = 0;

		pos = nextToken(url, pos, token);
		// LOGGER.fine(token.toString());

		if (!token.toString().equalsIgnoreCase("jdbc")) {
			LOGGER.severe("expected token jdbc in urlprefix " + url);
			return null; // jdbc: missing
		}

		pos = nextToken(url, pos, token); // Skip jtds
		// LOGGER.fine(token.toString());

		if (!token.toString().equalsIgnoreCase("postgresql")) {
			LOGGER.severe("expected token postgresql in urlprefix " + url);			
			return null; // postgresql: missing
		}

		pos = nextToken(url, pos, token); // Null token between : and //
		if (token.length() > 0) {
			LOGGER.severe("There should be no token between : and // in urlprefix " + url);			
			return null; // There should not be one!
		}

		pos = nextToken(url, pos, token); // Get server name
		// LOGGER.fine(token.toString());

		String host = token.toString();
		if (host == null || host.length() == 0) {
			LOGGER.severe("Missing server (host) name in urlprefix " + url);			
			return null; // Server name missing
		}
		props.setProperty("ServerName", host);
		
		if (url.charAt(pos - 1) == ':' && pos < url.length()) {
			pos = nextToken(url, pos, token); // Get port number
			// LOGGER.fine(token.toString());			

			try {
				int port = Integer.parseInt(token.toString());
				props.setProperty("PortNumber", Integer
						.toString(port));
			} catch (NumberFormatException e) {
				LOGGER.severe("Bad port number in urlprefix " + url);			
				return null; // Bad port number
			}
		}

		if (url.charAt(pos - 1) == '/' && pos < url.length()) {
			pos = nextToken(url, pos, token); // Get database name
			// LOGGER.fine(token.toString());			
			props.setProperty("DatabaseName", token.toString());
		}

		//
		// Process any additional properties in URL
		//
		
		// but we ignore anyway - pb
/*		
		while (url.charAt(pos - 1) == ';' && pos < url.length()) {
			pos = nextToken(url, pos, token);
			String tmp = token.toString();
			int index = tmp.indexOf('=');

			if (index > 0 && index < tmp.length() - 1) {
				props.setProperty(tmp.substring(0, index).toUpperCase(), tmp
						.substring(index + 1));
			} else {
				props.setProperty(tmp.toUpperCase(), "");
			}
		}
*/			
		return props;
	}

	/**
	 * Extract the next lexical token from the URL.
	 * 
	 * @param url
	 *            The URL being parsed
	 * @param pos
	 *            The current position in the URL string.
	 * @param token
	 *            The buffer containing the extracted token.
	 * @return The updated position as an <code>int</code>.
	 */
	private static int nextToken(String url, int pos, StringBuffer token) {
		token.setLength(0);

		while (pos < url.length()) {
			char ch = url.charAt(pos++);

			if (ch == ':' || ch == ';') {
				break;
			}

			if (ch == '/') {
				if (pos < url.length() && url.charAt(pos) == '/') {
					pos++;
				}

				break;
			}

			token.append(ch);
		}

		return pos;
	}

}
