package jp.sf.amateras.mirage.session;

import java.sql.Connection;
import java.sql.DriverManager;

import jp.sf.amateras.mirage.SqlManager;
import jp.sf.amateras.mirage.SqlManagerImpl;
import jp.sf.amateras.mirage.dialect.Dialect;
import jp.sf.amateras.mirage.dialect.HyperSQLDialect;
import jp.sf.amateras.mirage.dialect.MySQLDialect;
import jp.sf.amateras.mirage.dialect.OracleDialect;
import jp.sf.amateras.mirage.dialect.PostgreDialect;
import jp.sf.amateras.mirage.dialect.StandardDialect;
import jp.sf.amateras.mirage.exception.SessionException;
import jp.sf.amateras.mirage.provider.DefaultConnectionProvider;
import jp.sf.amateras.mirage.util.StringUtil;

/**
 * The default inplementation of {@link Session}.
 * <p>
 * This implementation set {@link Dialect} to {@link SqlManager} automatically by the JDBC connection URL.
 *
 * @author Naoki Takezoe
 */
public class JDBCSessionImpl implements Session {

	private SqlManager sqlManager;
	private DefaultConnectionProvider provider;
	private String driver;
	private String url;
	private String user;
	private String password;

	/**
	 * The constructor.
	 *
	 * @param driver the JDBC driver classname
	 * @param url the JDBC connection URL
	 * @param user the username
	 * @param password the password
	 */
	public JDBCSessionImpl(String driver, String url, String user, String password){
		this.driver = driver;
		this.url = url;
		this.user = user;
		this.password = password;

		sqlManager = new SqlManagerImpl();
		sqlManager.setDialect(getDialect());
		provider = new DefaultConnectionProvider();
		sqlManager.setConnectionProvider(provider);
	}

	private Dialect getDialect(){
		if(url.startsWith("jdbc:mysql:")){
			return new MySQLDialect();
		} else if(url.startsWith("jdbc:postgresql:")){
			return new PostgreDialect();
		} else if(url.startsWith("jdbc:oracle:")){
			return new OracleDialect();
		} else if(url.startsWith("jdbc:hsqldb:")){
			return new HyperSQLDialect();
		}
		return new StandardDialect();
	}

	/**
	 * {@inheritDoc}
	 */
	public void begin() throws SessionException {
		try {
			if(StringUtil.isNotEmpty(driver)){
				Class.forName(driver);
			}
			Connection conn = DriverManager.getConnection(url, user, password);
			conn.setAutoCommit(false);
			provider.setConnection(conn);
		} catch(Exception ex){
			throw new SessionException("Failed to begin transaction.", ex);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public void commit() throws SessionException {
		try {
			provider.getConnection().commit();
		} catch(Exception ex){
			throw new SessionException("Failed to commit transaction.", ex);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	public SqlManager getSqlManager() throws SessionException {
		return sqlManager;
	}

	/**
	 * {@inheritDoc}
	 */
	public void release() throws SessionException {
		if(provider instanceof DefaultConnectionProvider){
			((DefaultConnectionProvider) provider).releaseConnection();
		}
	}

	public void rollback() throws SessionException {
		try {
			provider.getConnection().rollback();
		} catch(Exception ex){
			throw new SessionException("Failed to rollback transaction.", ex);
		}
	}

}
