/*
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

package com.github.adejanovski.cassandra.jdbc;

import static com.github.adejanovski.cassandra.jdbc.Utils.HOST_REQUIRED;
import static com.github.adejanovski.cassandra.jdbc.Utils.NOT_SUPPORTED;
import static com.github.adejanovski.cassandra.jdbc.Utils.NO_INTERFACE;
import static com.github.adejanovski.cassandra.jdbc.Utils.PROTOCOL;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_CONSISTENCY_LEVEL;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_CQL_VERSION;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_DATABASE_NAME;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_PASSWORD;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_PORT_NUMBER;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_SERVER_NAME;
import static com.github.adejanovski.cassandra.jdbc.Utils.TAG_USER;
import static com.github.adejanovski.cassandra.jdbc.Utils.createSubName;

import java.io.PrintWriter;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLNonTransientConnectionException;
import java.util.Properties;
import java.util.logging.Logger;

import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;

public class CassandraDataSource implements ConnectionPoolDataSource, DataSource
{

    static
    {
        try
        {
            Class.forName("com.github.adejanovski.cassandra.jdbc.CassandraDriver");
        }
        catch (ClassNotFoundException e)
        {
            throw new RuntimeException(e);
        }
    }

    protected static final String description = "Cassandra Data Source";

    protected String serverName;

    protected int    portNumber = 9042;

    protected String databaseName;

    protected String user;

    protected String password;

    protected String version = null;
    
    protected String consistency = null;

    public CassandraDataSource(String host, int port, String keyspace, String user, String password, String version, String consistency)
    {
        if (host != null) setServerName(host);
        if (port != -1) setPortNumber(port);
        if (version != null) setVersion(version);
        if (consistency != null) setConsistency(consistency);
        setDatabaseName(keyspace);
        setUser(user);
        setPassword(password);
    }

    public String getDescription()
    {
        return description;
    }

    public String getServerName()
    {
        return serverName;
    }

    public void setServerName(String serverName)
    {
        this.serverName = serverName;
    }

    public String getVersion()
    {
        return version;
    }

    public void setVersion(String version)
    {
        this.version = version;
    }

    public String getConsistency()
    {
        return consistency;
    }

    public void setConsistency(String consistency)
    {
        this.consistency = consistency;
    }

    public int getPortNumber()
    {
        return portNumber;
    }

    public void setPortNumber(int portNumber)
    {
        this.portNumber = portNumber;
    }

    public String getDatabaseName()
    {
        return databaseName;
    }

    public void setDatabaseName(String databaseName)
    {
        this.databaseName = databaseName;
    }

    public String getUser()
    {
        return user;
    }

    public void setUser(String user)
    {
        this.user = user;
    }

    public String getPassword()
    {
        return password;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

    public CassandraConnection getConnection() throws SQLException
    {
        return getConnection(null, null);
    }

    public CassandraConnection getConnection(String user, String password) throws SQLException
    {
        Properties props = new Properties();
        
        this.user = user;
        this.password = password;
        
        if (this.serverName!=null) props.setProperty(TAG_SERVER_NAME, this.serverName);
        else throw new SQLNonTransientConnectionException(HOST_REQUIRED);
        props.setProperty(TAG_PORT_NUMBER, ""+this.portNumber);
        if (this.databaseName!=null) props.setProperty(TAG_DATABASE_NAME, this.databaseName);
        if (user!=null) props.setProperty(TAG_USER, user);
        if (password!=null) props.setProperty(TAG_PASSWORD, password);
        if (this.version != null) props.setProperty(TAG_CQL_VERSION, version);
        if (this.consistency != null) props.setProperty(TAG_CONSISTENCY_LEVEL, consistency);

        String url = PROTOCOL+createSubName(props);
        return (CassandraConnection) DriverManager.getConnection(url, props);
    }

    public int getLoginTimeout()
    {
        return DriverManager.getLoginTimeout();
    }

    public PrintWriter getLogWriter()
    {
        return DriverManager.getLogWriter();
    }

    public void setLoginTimeout(int timeout)
    {
        DriverManager.setLoginTimeout(timeout);
    }

    public void setLogWriter(PrintWriter writer)
    {
        DriverManager.setLogWriter(writer);
    }

    public boolean isWrapperFor(Class<?> iface)
    {
        return iface.isAssignableFrom(getClass());
    }

    public <T> T unwrap(Class<T> iface) throws SQLException
    {
        if (iface.isAssignableFrom(getClass())) return iface.cast(this);
        throw new SQLFeatureNotSupportedException(String.format(NO_INTERFACE, iface.getSimpleName()));
    }  
    
    public Logger getParentLogger() throws SQLFeatureNotSupportedException
    {
    	throw new SQLFeatureNotSupportedException(String.format(NOT_SUPPORTED));
    }

	@Override
	public PooledCassandraConnection getPooledConnection() throws SQLException
	{
		return new PooledCassandraConnection(getConnection());
	}

	@Override
	public PooledCassandraConnection getPooledConnection(String user, String password) throws SQLException
	{
		return new PooledCassandraConnection(getConnection(user, password));
	}
}