package cn.service.orm.sqlsam;

import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.orm.sqltoy.SqlExecuteStat;
import cn.orm.sqltoy.SqlToyContext;
import cn.orm.util.builder.base.SQLAssemblyUtil;
import cn.orm.util.metadata.DBMetaData;
import cn.util.core.IsKit;
import cn.util.core.LogKit;
import cn.util.core.TypeKit;
import cn.util.string.NamingKit;

/** 资源关闭顺序
 *1、关闭resultSet
 *2、关闭statement preparedStatement
 *3、关闭connection
 */
@Service
public class SqlToySamDao 
{
	public int executeBatch(String[] sql) 
	{
		Boolean commit = false;
		int num = -1;
		try {
			if (initDBCon() && !isClosed()) {
				try {
					num = _executeBatch(sql);
					//手动提交执行，并打开自动提交.对MyISAM不存储引擎
					this.commit();
					commit = true;
				} catch (Exception e) {
					rollback();
				} finally {
					if (!commit) {
						closeCon();
					}
					commit = true;
				}
			}
		} catch (Exception e) {
			LogKit.info(e.getMessage());
		}
		return num;
	}

	public int insertAndGenIndex(String tableName, Map<String, Object> mapRecord)
	{
		int num = -1;
		if( IsKit.isNullOrEmpty(tableName) || IsKit.isNullOrEmpty(mapRecord)){
			return num;
		}
		Boolean commit = false;		
		try {
			if (initDBCon() && !isClosed()) {
				try {
					num = _insertAndAutokey(tableName,mapRecord, true);
					//手动提交执行，并打开自动提交.对MyISAM不存储引擎
					this.commit();
					commit = true;
				} catch (Exception e) {
					rollback();
				} finally {
					if (!commit) {
						closeCon();
					}
					commit = true;
				}
			}
		} catch (Exception e) {
			LogKit.info(e.getMessage());
		}
		return num;
	}

	//插入一条记录 @param autoInsertKey 值为true时，系统自动插入主键; 返回的是主键值
	private int _insertAndAutokey(String tableName, Map<String, Object> mapRecord, boolean autoInsertKey) throws Exception	
	{
		int num = -1;
		String[] tableKeys = DBMetaData.getKeys(tableName);//表中的主键字段集
		if(IsKit.isNullOrEmpty(tableKeys)) {
			return num;
		}
		mapRecord = NamingKit.camelToUnderline(mapRecord);
		String[] tableFields = DBMetaData.getColumnsName(tableName);//表中的字段名的集合
		//作为创建preparedStatementSQL语句的依据
		Map<String, Object> _m = mapRecord;
		
		Object[] recordFields = (_m.keySet()).toArray(); //获取记录里的字段名的集合
		if(IsKit.isNotNullOrEmpty(recordFields)) {
			int loop = recordFields.length;
			for (int i = 0; i < loop; i++)
			{
				if (!IsKit.isInFields(tableFields, recordFields[i].toString())) {
					_m.remove(recordFields[i].toString());//移除无效字段， 查看记录中的字段在表中是否存在，如果不存在，则移除到
				}
			}
		}
		Object[] fields = (_m.keySet()).toArray(); //过滤后的有效字段
		String[] values = new String[fields.length]; //保存通配符'?'
		if(IsKit.isNotNullOrEmpty(fields)) {
			int loop = fields.length;
			for (int i = 0; i < loop; i++) {
				values[i] = "?";
			}
		}
		String sql_field = TypeKit.arryToStr(fields, ",");
		String sql_values = TypeKit.arryToStr(values, ",");
		if (IsKit.isNullOrEmpty(sql_field)|| IsKit.isNullOrEmpty(sql_values)) {
			return num;	
		}
		String preparedStatementInsert = "INSERT INTO " + tableName + " (" + sql_field + ") VALUES (" + sql_values + ")";//合成preparedStatement语句
		try {
			preStmt = con.prepareStatement(preparedStatementInsert,Statement.RETURN_GENERATED_KEYS);
			buildSQLUtil.setSQLParamValue(tableName,mapRecord,fields,preStmt,-1);
			num = preStmt.executeUpdate();
			if (autoInsertKey){
				rs = preStmt.getGeneratedKeys();//获取自增长的列的新值
				//不能直接取RsultSet结果集中记录信息.比如：rs.getString("name");会报Illegal operation on empty result set错
				while(rs.next()){
					num = rs.getInt(1);
				}
				mapRecord.put(tableFields[0],num);//自动插入数值型主键值
			}
		} 
		finally {
			this.closePreStmt();
		}
		return num;
	}

	private int _executeBatch(String[] sql) throws SQLException 
	{
		int num = 0;
		if(IsKit.isNullOrEmpty(sql)) {
			return num;
		}		
		SqlExecuteStat.start("SqlToySamDao","executeBatch",sqlToyContext.isDebug());
		//SqlExecuteStat.setDialect(Dialect.MYSQL);
		String sqlStr = "";
		try {
			stmt = con.createStatement();
			if(IsKit.isNullOrEmpty(stmt)) { 
				return num;
			}
			int loop = sql.length;
			for(int i = 0; i < loop; i++){
				sqlStr = sqlStr + ";" +sql[i];
				stmt.addBatch(sql[i]);
			}
			//执行批处理语句.刷新缓存区
			int[] rowCounts = stmt.executeBatch();
			//影响的行数
			if(IsKit.isNotNullOrEmpty(rowCounts)) {
				loop = rowCounts.length;
				for (int j = 0; j < loop; j++) {
					//成功执行，但受影响的行数是未知的
					if (rowCounts[j] == Statement.SUCCESS_NO_INFO) {
						num = num + 1;//随意设置
					} 
					 //指示未能成功执行命
					else if (rowCounts[j] == Statement.EXECUTE_FAILED) {
						throw new SQLException("The batched statement at index " + j + " failed to execute.");  
					} 
					else { 
						num += rowCounts[j]; 
					}
				}
				SqlExecuteStat.showSql("SQL语句", sqlStr, null);
				SqlExecuteStat.debug("执行结果", "操作影响记录量:{} 条!", num);
			}
		} catch (BatchUpdateException e) {throw e;}
		finally {
			stmt.clearBatch();//2018xbm 清除之前的数据
			this.closeStmt();
			SqlExecuteStat.destroy();
		} 
		return num;
	}

	public SqlToySamDao(DataSource dataSource) throws Exception
	{
		con = org.springframework.jdbc.datasource.DataSourceUtils.getConnection(dataSource);
		if(IsKit.isNull(con)) {
			throw new Exception();
		}
		buildSQLUtil = new SQLAssemblyUtil();
		autoCommit = true;//默认自动提交
		failCommit = true;
	}

	/*保留
	@Autowired
	private SqlToyContextProperties properties;
	@Autowired
	private DataSource dataSource;

	 */
	@Autowired
	private SqlToyContext sqlToyContext; 

	//获取数据库连接
	private Boolean initDBCon()
	{
		DataSource dataSource = sqlToyContext.getDefaultDataSource();
		if(IsKit.isNull(dataSource)) {
			return false;
		}
		con = org.springframework.jdbc.datasource.DataSourceUtils.getConnection(dataSource);
		if(IsKit.isNull(con)) {
			return false;
		}
		try {
			setAutoCommit(false);
		} catch (Exception e) {
			return false;
		}			
		return true;
	}

	//2关闭 Statement stmt
	private void closeStmt() throws SQLException 
	{
		try {
			if (IsKit.isNotNullOrEmpty(stmt)){ 
				stmt.cancel(); //取消Statement中的SQL语句指定的数据库操作命令
			}
		}catch (SQLException e) {
			throw e;
		}finally{
			try {
				if (IsKit.isNotNullOrEmpty(stmt)) {
					stmt.close(); //关闭Statement语句指定的数据库连接
				}
			} catch (SQLException e) {
				throw e;
			}
			stmt = null;//是否需要??
		}
	}
	//2关闭PreparedStatement PreStmt
	private void closePreStmt() throws SQLException 
	{
		try {
			if (IsKit.isNotNullOrEmpty(preStmt)){
				preStmt.cancel();
			}
		}
		catch (SQLException e) {
			throw e;
		}
		finally{
			try {
				if (IsKit.isNotNullOrEmpty(preStmt)) { 
					preStmt.close();
				}
			} catch (SQLException e) {
				throw e;
			}
			preStmt = null;//是否需要	
		}
	}
	/** 取消。先回滚、后关闭。*/
	public void cancel() {
		try {
			if (initDBCon() && !isClosed()) {
				try {
					rollback();
				} finally {
					closeCon();
				}
			}
		} catch (Exception e) {
			LogKit.info(e);
		}
	}
	public void rollback() throws SQLException
	{
		if (!IsKit.isNull(con) && !con.isClosed())
		{
			if (!this.autoCommit){
				if (failCommit) {//true表示提交失败
					con.rollback();
					con.close();
					autoCommit = true;
				}
			}
		}
	}
	public void closeCon() throws SQLException  
	{
		if (IsKit.isNull(con)) {  
			return;
		}
		try {
		}finally {
			if (!con.isClosed()) {
				con.close();//立即释放连接对象的数据库和JDBC资源
			}
			con = null;  //Make sure we don't close it twice
		}
	}

	public Connection getCon(){
		return this.con;
	}  
	public void setCon(Connection con){
		this.con = con;
	} 

	public boolean isClosed() throws SQLException
	{
		if (IsKit.isNull(con)) {
			return true;
		}
		return con.isClosed();//已关闭，返回true
	}
	/**
	 * 用于判断数据库连接是否使用了事务
	 * @return 是否提交事务
	 * @throws SQLException 抛出SQL异常
	 */
	public Boolean isAutoCommit() throws SQLException {
		return con.getAutoCommit();
	}
	/**
	 * 设定事务处理
	 * @param autoCommit false开启事务，true关闭事务
	 * @throws SQLException 抛出SQL异常
	 */
	public void setAutoCommit(boolean autoCommit) throws SQLException
	{
		if (IsKit.isNotNullOrEmpty(con)) {
			con.setAutoCommit(autoCommit);
		}
		this.autoCommit = autoCommit;
	}

	public void commit() throws SQLException
	{
		if (IsKit.isNotNullOrEmpty(con) && !con.isClosed())
		{
			if (!autoCommit) {//  if (!con.getAutoCommit())  2017
				try {
					con.commit();//执行完后，手动提交事务
					autoCommit = true; //打开自动提交
					con.setAutoCommit(autoCommit);
					failCommit = false;
				} finally {
					if (failCommit) {//true表示提交失败
						rollback();
					}
					closeCon();
				}
			}
		}
	}

	private SQLAssemblyUtil buildSQLUtil = null;
	private java.sql.ResultSet rs;
	//提供基本的 SQL 操作. 适合静态SQL语句, 且传入的 SQL语句无法接受参数.
	private java.sql.Statement stmt; 
	//可以在 SQL 中传递参数, 适合多次使用的 SQL 语句.
	private java.sql.PreparedStatement preStmt;
	//保存为用户建立的数据库连接
	private Connection con; 
	//false禁止自动提交，true自动提交
	private boolean autoCommit;
	//true表示提交失败
	private boolean failCommit;
}
