package org.jfox.dao;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;

import org.jfox.dao.mapping.TableMapping;

/**
 * @author <a href="mailto:yangyong@ufsoft.com.cn">Young Yang</a>
 */

public abstract class AbstractDAO {
    protected Connection conn = null;

    protected TableMapping mapping = null;

    /**
     * init connection
     *
     * @return
     * @throws SQLException
     */
    protected abstract Connection initConnection() throws SQLException;

    public synchronized Connection getConnection() throws DAOException {
        if(conn == null) {
            try {
                conn = initConnection();
            }
            catch(Exception e) {
                throw new DAOException("init connection failed.", e);
            }
        }
        else {
            try {
                if(conn.isClosed()) {
                    conn = initConnection();
                }
            }
            catch(Exception e) {
                throw new DAOException("init connection failed.", e);
            }
        }
        return conn;
    }

    protected TableMapping initMapping(Class pClass) throws DAOException {
        Connection conn = getConnection();
        return initMapping(pClass, conn);
    }

    protected TableMapping initMapping(Class pClass, Connection conn) throws DAOException {
        String table = DAOUtil.getTableName(pClass, null, conn);
        return MappingUtil.mapTable(pClass, table, conn);
    }

    protected TableMapping getMapping() throws DAOException {
        if(mapping == null) {
            throw new DAOException("Table mapping not initialized.");
        }
        return mapping;
    }

    protected synchronized TableMapping getMapping(Class pClass) throws DAOException {
        if(mapping != null) {
            if(!mapping.getMappingClass().isAssignableFrom(pClass)) {
                throw new DAOException("Mapping has initialized, but type is " + mapping.getMappingClass().getName() + ", not " + pClass.getName());
            }
        }
        else {
            mapping = initMapping(pClass);
        }
        return mapping;
    }

    protected synchronized TableMapping getMapping(Class pClass, Connection conn) throws DAOException {
        if(mapping != null) {
            if(!mapping.getMappingClass().isAssignableFrom(pClass)) {
                throw new DAOException("Mapping has initialized, but type is " + mapping.getMappingClass().getName() + ", not " + pClass.getName());
            }
        }
        else {
            mapping = initMapping(pClass, conn);
        }
        return mapping;
    }

    protected int insert(Object pObject) throws DAOException {
        return insert(pObject, getConnection());
    }

    protected int insert(Object pObject, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pObject.getClass(), conn);
        String sql = SqlUtil.generateInsertStatement(tm);
        return MappingUtil.insert(tm, pObject, sql, conn);
    }

    protected int[] insertBatch(Object[] pObjects) throws DAOException {
        Connection conn = getConnection();
        return insertBatch(pObjects, conn);
    }


    protected int[] insertBatch(Object[] pObjects, Connection conn) throws DAOException {
        if(pObjects == null || pObjects.length == 0) return new int[]{};
        TableMapping tm = getMapping(pObjects[0].getClass(), conn);
        String sql = SqlUtil.generateInsertStatement(tm);
        return MappingUtil.insertBatch(tm, pObjects, sql, conn);
    }

    protected int update(Object pObject) throws DAOException {
        return update(pObject, getConnection());
    }

    protected int update(Object pObject, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pObject.getClass(), conn);
        String sql = SqlUtil.generateUpdateStatement(tm);
        return MappingUtil.update(tm, pObject, sql, conn);
    }

    public int updateByPrimaryKey(Object pObject, Object originPK) throws DAOException {
        return updateByPrimaryKey(pObject, originPK, getConnection());
    }

    public int updateByPrimaryKey(Object pObject, Object originPK, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pObject.getClass(), conn);
        String sql = SqlUtil.generateUpdateStatement(tm);
        return MappingUtil.updateByPrimaryKey(tm, pObject, originPK, sql, conn);
    }

    protected int[] updateBatch(Object[] pObjects) throws DAOException {
        Connection conn = getConnection();
        return updateBatch(pObjects, conn);
    }


    protected int[] updateBatch(Object[] pObjects, Connection conn) throws DAOException {
        if(pObjects == null || pObjects.length == 0) return new int[]{};
        TableMapping tm = getMapping(pObjects[0].getClass(), conn);
        String sql = SqlUtil.generateUpdateStatement(tm);
        return MappingUtil.updateBatch(tm, pObjects, sql, conn);
    }

    protected int delete(Object pObject) throws DAOException {
        return delete(pObject, getConnection());
    }

    protected int delete(Object pObject, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pObject.getClass(), conn);
        String sql = SqlUtil.generateDeleteStatement(tm);
        return MappingUtil.delete(tm, pObject, sql, conn);
    }

    protected int deleteByPrimaryKey(Class pClass, Object pk) throws DAOException {
        return deleteByPrimaryKey(pClass, pk, getConnection());
    }

    protected int deleteByPrimaryKey(Class pClass, Object pk, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pClass);
        String sql = SqlUtil.generateDeleteStatement(tm);
        return MappingUtil.deleteBySpecificPrimaryKey(tm, pk, sql, getConnection());
    }

    protected int[] deleteBatch(Object[] pObjects) throws DAOException {
        Connection conn = getConnection();
        return deleteBatch(pObjects, conn);
    }

    protected int[] deleteBatch(Object[] pObjects, Connection conn) throws DAOException {
        if(pObjects == null || pObjects.length == 0) return new int[]{};
        TableMapping tm = getMapping(pObjects[0].getClass(), conn);
        String sql = SqlUtil.generateDeleteStatement(tm);
        return MappingUtil.deleteBatch(tm, pObjects, sql, conn);
    }

    protected int[] deleteBatchByPrimaryKeys(Class pClass, Object[] pks) throws DAOException {
        if(pks == null || pks.length == 0) return new int[]{};
        return deleteBatchByPrimaryKeys(pClass, pks, getConnection());
    }

    protected int[] deleteBatchByPrimaryKeys(Class pClass, Object[] pks, Connection conn) throws DAOException {
        if(pks == null || pks.length == 0) return new int[]{};
        TableMapping tm = getMapping(pClass, conn);
        String sql = SqlUtil.generateDeleteStatement(tm);
        return MappingUtil.deleteBatchByPrimaryKeys(tm, pks, sql, conn);
    }

    protected Object fetch(Class pClass, String sql) throws DAOException {
        return fetch(pClass,sql,getConnection());
    }

    protected Object fetch(Class pClass, String sql, Connection conn) throws DAOException{
        TableMapping tm = getMapping(pClass);
        return MappingUtil.fetch(tm,sql,conn);
    }

    protected Object fetchByPrimaryKey(Class pClass, Object pk) throws DAOException {
        return fetchByPrimaryKey(pClass,pk,getConnection());
    }

    protected Object fetchByPrimaryKey(Class pClass, Object pk, Connection conn) throws DAOException{
        TableMapping tm = getMapping(pClass);
        String sql = SqlUtil.generateSelectStatementByPrimaryKey(tm);
        return MappingUtil.fetchByPrimaryKey(tm,pk,sql,conn);
    }

    protected Collection fetchBatch(Class pClass, String sql) throws DAOException {
        return fetchBatch(pClass,sql,getConnection());
    }

    protected Collection fetchBatch(Class pClass, String sql, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pClass);
        return MappingUtil.fetchBatch(tm,sql,conn);
    }

    protected Collection fetchBatchByPrimaryKeys(Class pClass, Object[] pks) throws DAOException {
        return fetchBatchByPrimaryKeys(pClass,pks,getConnection());
    }

    protected Collection fetchBatchByPrimaryKeys(Class pClass, Object[] pks, Connection conn) throws DAOException {
        TableMapping tm = getMapping(pClass);
        String sql = SqlUtil.generateSelectStatementByPrimaryKey(tm);
        return MappingUtil.fetchBatchByPrimaryKeys(tm,pks,sql,conn);
    }


}

