/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights reserved. <a>http://www.cqcis.com</a>
 */
package com.crunii.micro.domain.common.jdbc;

import com.crunii.micro.common.dto.PageReq;
import com.crunii.micro.common.dto.PageRsp;
import com.crunii.micro.domain.common.jdbc.dto.AbsSetProps;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.RowMapperResultSetExtractor;
import org.springframework.util.Assert;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 田平 create 2013-3-14下午4:55:43
 */
public class BaseJdbcTemplate extends JdbcTemplate {

	private boolean enableEncrypt = false;

	private String dbType;

	public BaseJdbcTemplate(DataSource dataSource, String dbType) {
		super(dataSource);
		Assert.hasText(dbType, "dbType not allowed null!");
		this.dbType = dbType;
	}

	public String getDbType() {
		return dbType;
	}

	/**
	 * <pre>
	 * 查询返回基本数据类型对象，如果没有数据则返回null，返回多于一行数据异常，只能返回小于等于1行数据 只能返回一个字段
	 * 如果返回字符串，针对informix，数据库为""空字符串，反回的也是null，oracle不存在这个问题
	 * </pre>
	 *
	 * @param clazzTo
	 * @param querySql
	 * @param params
	 * @return
	 */
	public <T> T queryForRawObject(Class<T> clazzTo, String querySql, Object... params) {
		List<T> list = query(querySql, RowMapperUtil.getSingleRW(clazzTo, querySql, this.enableEncrypt), params);
		if (list.size() == 1) {
			return list.get(0);
		} else if (list.size() == 0) {
			return null;
		} else {
			throw new IncorrectResultSizeDataAccessException(1, list.size());
		}
	}

	public <T> List<T> queryForRawObjects(Class<T> clazzTo, String querySql, Object... params) {
		List<T> list = query(querySql, RowMapperUtil.getSingleRW(clazzTo, querySql, this.enableEncrypt), params);
		return list;
	}

	/**
	 * 返回一个查询列表, 列表中的对象类型为clazzTo参数指定的类型对象。 改方法适用于查询SQL中使用?作为参数占位符的SQL语句
	 *
	 * @param querySql
	 * @param clazzTo
	 * @param params
	 * @return
	 */
	public <T> List<T> queryForBeans(Class<T> clazzTo, String querySql, Object... params) {
		List<T> list = query(querySql, RowMapperUtil.getBeanPropertyRW(clazzTo, querySql, this.enableEncrypt), params);
		list.forEach(t -> {
			if (t instanceof AbsSetProps) {
				AbsSetProps absSetProps = (AbsSetProps) t;
				((AbsSetProps) t).setDomainForUpdate(true);
			}
		});
		return list;
	}

	/**
	 * 返回单个实体对象，如果没有数据返回null，如果数据多于1条抛异常
	 *
	 * @param clazzTo
	 * @param querySql
	 * @param params
	 * @return
	 */
	public <T> T queryForBean(Class<T> clazzTo, String querySql, Object... params) {
		List<T> beans = queryForBeans(clazzTo, querySql, params);
		if (beans.size() == 0) {
			return null;
		} else if (beans.size() == 1) {
			return beans.get(0);
		} else {
			throw new IncorrectResultSizeDataAccessException(1, beans.size());
		}
	}

	public <T> T queryForObject(String sql, Object[] args, int[] argTypes, RowMapper<T> rowMapper) throws DataAccessException {

		List<T> results = query(sql, args, argTypes, new RowMapperResultSetExtractor<T>(rowMapper, 1));
		return DataAccessUtils.singleResult(results);
	}

	public <T> T queryForObject(String sql, Object[] args, RowMapper<T> rowMapper) throws DataAccessException {
		List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
		return DataAccessUtils.singleResult(results);
	}

	public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args) throws DataAccessException {
		List<T> results = query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1));
		return DataAccessUtils.singleResult(results);
	}

	public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
		List<T> results = query(sql, rowMapper);
		return DataAccessUtils.singleResult(results);
	}

	public int queryForInt(String sql, Object... args) {
		Integer result = queryForRawObject(Integer.class, sql, args);
		return result == null ? 0 : result;
	}

	/**
	 * @return the enableEncrypt
	 */
	public boolean isEnableEncrypt() {
		return enableEncrypt;
	}

	/**
	 * @param enableEncrypt the enableEncrypt to set
	 */
	public void setEnableEncrypt(boolean enableEncrypt) {
		this.enableEncrypt = enableEncrypt;
	}

	/**
	 * 分页查询base
	 * @param req
	 * @param sql
	 * @param countSql
	 * @param clazz
	 * @param params
	 * @param <T>
	 * @return
	 */
	public <T> PageRsp<T> queryPageBase(PageReq<?> req, String sql, String countSql, Class<T> clazz, List<Object> params){
		StringBuilder sb = new StringBuilder(sql);
		StringBuilder sbCount = new StringBuilder(countSql);

		int total = queryForInt(sbCount.toString(), params.toArray());
		List<T> values = null;
		if (total > 0) {
			sb.append(" limit ? offset ? ");
			params.add(req.getPageSize());
			params.add(req.getMyStart());
			values = queryForBeans(clazz, sb.toString(), params.toArray());
		} else {
			values = new ArrayList<T>(0);
		}
		PageRsp<T> page = new PageRsp<T>();
		page.setPageNo(req.getPageNo());
		page.setPageSize(req.getPageSize());
		page.setTotalNum(total);
		page.setResult(values);
		// 为手机计算是否由下一页
		page.next();

		return page;
	}

}
