/*
 *    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;

import java.io.IOException;
import java.nio.charset.Charset;
import java.sql.SQLException;
import java.util.Map;
import java.util.logging.Logger;

import org.geotools.data.DataSourceException;
import org.geotools.data.DataStore;
import javax.sql.DataSource;
// import org.geotools.data.jdbc.ConnectionPool;
import org.geotools.data.jdbc.datasource.DataSourceUtil;
import org.geotools.data.jdbc.datasource.ManageableDataSource;
import org.geotools.data.sql.ViewRegisteringFactoryHelper;
import org.geotools.factory.AbstractFactory;

/**
 * Creates a Geometryless JDBC based on the conection params.
 * 
 * <p>
 * This factory should be registered in the META-INF/ folder, under services/
 * in the DataStoreFactorySpi file.
 * </p>
 *
 * @author Rob Atkinson, Social Change Online
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/unsupported/geometryless/src/main/java/org/geotools/data/geometryless/JDBCDataStoreFactory.java $
 */
public class JDBCDataStoreFactory extends AbstractFactory
    implements org.geotools.data.DataStoreFactorySpi {
    	
    private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.data.geometryless");


    /** Specified JDBC driver class. */
    static final Param  DRIVER = new Param("driver", String.class,
            "Java Class name of installed ConnectionPool driver", true, "org.geotools.data.geometryless.wrapper.PGConnectionPool");
            
               /** Specified JDBC driver class calling URL */
   static final Param  URLPREFIX  = new Param("urlprefix", String.class,
            "eg jdbc:mysql://", true, "jdbc:postgresql://localhost:5432/postgres");

    /** Param, package visibiity for JUnit tests */
    static final Param DBTYPE = new Param("dbtype", String.class,
            "must be 'jdbc'", true, "jdbc");



    /** Param, package visibiity for JUnit tests */
    static final Param USER = new Param("user", String.class,
            "user name to login as", true, "");

    /** Param, package visibiity for JUnit tests */
    static final Param PASSWD = new Param("passwd", String.class,
            "password used to login", true, "");


    /**
     * Param, package visibiity for JUnit tests.
     * 
     * <p>
     * Example of a non simple Param type where custom parse method is
     * required.
     * </p>
     * 
     * <p>
     * When we convert to BeanInfo custom PropertyEditors will be required for
     * this Param.
     * </p>
     */
    static final Param CHARSET = new Param("charset", Charset.class,
            "character set", false, Charset.forName("ISO-8859-1")) {
            public Object parse(String text) throws IOException {
                return Charset.forName(text);
            }

            public String text(Object value) {
                return ((Charset) value).name();
            }
        };

    /** Param, package visibiity for JUnit tests */
    static final Param NAMESPACE = new Param("namespace", String.class,
            "namespace prefix used", false);

    /** Param, package visibiity for JUnit tests */
    static final Param SCHEMA = new Param("schema", String.class,
            "database schema", false);

    /** Array with all of the params */
    static final Param[] arrayParameters = {
        DBTYPE, SCHEMA, USER, PASSWD, CHARSET, NAMESPACE,DRIVER,URLPREFIX
    };

   /**
     * Creates a new instance of - this constructor needed for factory finder apparently
     */
    public JDBCDataStoreFactory() {

}

    /**
     * Creates a new instance of PostgisDataStoreFactory
     */
    public JDBCDataStoreFactory( Map hints ) {
	if( hints != null )
	        this.hints.putAll( hints );
    }

    /**
     * Checks to see if all the postgis params are there.
     * 
     * <p>
     * Should have:
     * </p>
     * 
     * <ul>
     * <li>
     * dbtype: equal to postgis
     * </li>
     * <li>
     * host
     * </li>
     * <li>
     * user
     * </li>
     * <li>
     * passwd
     * </li>
     * <li>
     * database
     * </li>
     * <li>
     * charset
     * </li>
     * </ul>
     * 
     *
     * @param params Set of parameters needed for a jdbc data store.
     *
     * @return <code>true</code> if dbtype equals jdbc, and contains keys
     *         for host, user, passwd, and database.
     */
    public boolean canProcess(Map params) {
        Object value;

        if (params != null) {
            for (int i = 0; i < arrayParameters.length; i++) {
                if (!(((value = params.get(arrayParameters[i].key)) != null)
                        && (arrayParameters[i].type.isInstance(value)))) {
                    if (arrayParameters[i].required) {
                    	LOGGER.config("JDBCDataStoreFactory: canProcess() Cannot find param " + arrayParameters[i].key + ":" + arrayParameters[i].type + value );
                        return (false);
                    }
                }
            }
        } else {
                   	LOGGER.finer("JDBCDataStoreFactory: can Process Cannot find params " );
            return (false);
        }

  
        if (!(((String) params.get("dbtype")).equalsIgnoreCase("jdbc"))) {
            return (false);
        } else {
            return (true);
        }
    }

    /**
     * Construct a postgis data store using the params.
     *
     * @param params The full set of information needed to construct a live
     *        data source.  Should have  dbtype equal to postgis, as well as
     *        host, user, passwd, database, and table.
     *
     * @return The created DataSource, this may be null if the required
     *         resource was not found or if insufficent parameters were given.
     *         Note that canProcess() should have returned false if the
     *         problem is to do with insuficent parameters.
     *
     * @throws IOException See DataSourceException
     * @throws DataSourceException Thrown if there were any problems creating
     *         or connecting the datasource.
     */
    public DataStore createDataStore(Map params) throws IOException {
        if (canProcess(params)) {
        } else {
            throw new IOException("The parameteres map isn't correct!!");
        }

 //       String host = (String) HOST.lookUp(params);
        String user = (String) USER.lookUp(params);
        String passwd = (String) PASSWD.lookUp(params);
 //       String port = (String) PORT.lookUp(params);
 //       String database = (String) DATABASE.lookUp(params);
        Charset charSet = (Charset) CHARSET.lookUp(params);
        String schema = (String) SCHEMA.lookUp( params ); 

        String namespace = (String) NAMESPACE.lookUp(params);
        String driver =   (String) DRIVER.lookUp(params);
        String urlprefix =   (String) URLPREFIX.lookUp(params);

        // Try processing params first so we can get an error message
        // back to the user
        //
        if (!canProcess(params)) {
            return null;
        }
/*
 * 
 *   All this stuff replaced with the  DataSource implementation
 *   
 *   which is basically exactly the same pattern here...
 *  
 *   JDBCConnectionFactory connFact = new JDBCConnectionFactory( urlprefix, driver );

 //  MySQLConnectionFactory connFact = new MySQLConnectionFactory(host,            Integer.parseInt(port), database);
           
        connFact.setLogin(user, passwd);

        if (charSet != null) {
            connFact.setCharSet(charSet.name());
        }

        ConnectionPool pool;

        try {
            pool = connFact.getConnectionPool();
            
            java.sql.Connection c = pool.getConnection();
            if( c == null )
            {
          	throw new SQLException("Pool created but connection null ");
            }
            else {
             	c.close();
            }
        } catch (SQLException e) {
            throw new DataSourceException("Could not create connection", e);
        }
*/

        DataSource dataSource = DataSourceUtil.buildDefaultDataSource(urlprefix, driver, user, passwd, null);
        
        JDBCDataStore dataStore;
        if (namespace != null) {
            dataStore = new JDBCDataStore(dataSource, schema, namespace);
        } else {
            dataStore = new JDBCDataStore(dataSource);
        }
        
        ViewRegisteringFactoryHelper.registerSqlViews(dataStore, params);
        
        return dataStore;
    }

 
    
    /**
     * The datastore  cannot create a new database.
     *
     * @param params
     *
     *
     * @throws IOException See UnsupportedOperationException
     * @throws UnsupportedOperationException Cannot create new database
     */
    public DataStore createNewDataStore(Map params) throws IOException {
        throw new UnsupportedOperationException(
            "Generic JDBC datastore cannot create a new Database");
    }

    /**
     * Describe the nature of the datasource constructed by this factory.
     *
     * @return A human readable description that is suitable for inclusion in a
     *         list of available datasources.
     */
    public String getDescription() {
        return "Generic JDBC database";
    }
   
    public String getDisplayName() {
        return "GeometrylessJDBC - no geometry per feature";
    }
    
//    	public DataSourceMetadataEnity createMetadata( Map params ) throws IOException {
//	    String host = (String) HOST.lookUp(params);
//        String user = (String) USER.lookUp(params);
//        String port = (String) PORT.lookUp(params);
//        String database = (String) DATABASE.lookUp(params);
//        return new DataSourceMetadataEnity( host+":"+port, database, "Connection to "+getDisplayName()+" on "+host+" as "+user );
//	}
    /**
     * Test to see if this datastore is available, if it has all the
     * appropriate libraries to construct a datastore.  This datastore just
     * returns true for now.  This method is used for gui apps, so as to not
     * advertise data store capabilities they don't actually have.
     *
     * @return <tt>true</tt> if and only if this factory is available to create
     *         DataStores.
     */
    public boolean isAvailable() {
  /*       try {
             Class.forName(DRIVER);
        } catch (ClassNotFoundException cnfe) {
            return false;
        }
   */
        return true;
    }

    /**
     * Describe parameters.
     *
     *
     * @see org.geotools.data.DataStoreFactorySpi#getParametersInfo()
     */
    public Param[] getParametersInfo() {
        return new Param[] {
        		  DBTYPE, SCHEMA, USER, PASSWD, CHARSET, NAMESPACE,DRIVER,URLPREFIX
  
        };
    }
}
