package com.platform.utils.page;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.log4j.Logger;

import com.platform.utils.Configs;

/** 分页拦截器接口的实现--（现只拦截 mysql）
 * @author chen
 *
 */
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class}),  
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})  
public class PageInterceptor implements Interceptor {

	@SuppressWarnings("static-access")
	public static Logger log = Configs.CONSOLE_LOGGER.getLogger(PageInterceptor.class);
	
    /** 用户提供分页计算条数后缀 */
    private static final String SELECT_ID="page";
 
    /* 拦截方法
     * (non-Javadoc)
     * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        
        if (invocation.getTarget() instanceof StatementHandler) {  
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();  
            MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);  
            MappedStatement mappedStatement=(MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
            String selectId=mappedStatement.getId();
            if(selectId.substring(selectId.lastIndexOf(".")+1).toLowerCase().endsWith(SELECT_ID)){
                BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");  
                // 分页参数作为参数对象parameterObject的一个属性  
                String sql = boundSql.getSql();
                Page co=(Page)(boundSql.getParameterObject());
                // 重写 Sql语句  
                log.info(sql.replaceAll("\n", "").replaceAll("\t\t", "\t"));
                String countSql=concatCountSql(sql);
                Connection connection = (Connection) invocation.getArgs()[0];  
                
                PreparedStatement countStmt = null;  
                ResultSet rs = null;  
                int totalCount = 0;  
                try { 
                	//绑定数据
                	countStmt = connection.prepareStatement(countSql);  
                    BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,  
                            boundSql.getParameterMappings(), boundSql.getParameterObject());  
                    setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());  
                    //执行
                    rs = countStmt.executeQuery();  
                    if (rs.next()) {  
                        totalCount = rs.getInt(1);  
                    } 
                    
                } catch (Exception e) {  
                	log.error(e);
                } finally {  
                    try {  
                    	if (null != rs) {
                    		rs.close();  
						}
                    	if (null != countStmt) {
                    		countStmt.close();  
						}
                    } catch (Exception e) {  
                    	log.error(e);
                    }  
                }  
                //绑定count
                co.setTotleSize(totalCount);
                int totalPage = totalCount/co.getLimit();
                if (totalCount > totalPage*co.getLimit()) {
                	totalPage+=1;
				}
                co.setTotlePage(totalPage);
                if (co.getCurrentPageNum() > co.getTotlePage()) {
                	co.setCurrentPageNum(co.getTotlePage());
                }
                String pageSql=concatPageMySql(sql,co);
                metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);            
            }
        } 
        
        return invocation.proceed();
    }
    
    /**  
     * <一句话功能简述> 重写sql
     * <功能详细描述>
     * @param sql
     * @return
     * @see [类、类#方法、类#成员]
     */
    public String concatCountSql(String sql){
        StringBuffer sb=new StringBuffer("select count(*) from ");
        sql=sql.toLowerCase();
        if(sql.lastIndexOf("order")>sql.lastIndexOf(")")){
            sb.append(sql.substring(sql.indexOf("from")+4, sql.lastIndexOf("order")));
        }else{
            sb.append(sql.substring(sql.indexOf("from")+4));
        }
        return sb.toString();
    }
    
    /**
     * <一句话功能简述> 重写sql
     * <功能详细描述>
     * @param sql
     * @param page
     * @return
     * @see [类、类#方法、类#成员]
     */
    public String concatPageMySql(String sql,Page page){
        StringBuffer sb=new StringBuffer();
        sb.append(sql);
        int size = page.getLimit();
        int index = page.getCurrentPageNum();
        if (index > 1) {
        	index = index -1;
        	if (page.getTotlePage() == page.getCurrentPageNum()) {
        		sb.append(" limit ").append(size*index).append(" , ").append(page.getTotleSize()-(size*index));
			}
        	else {
        		sb.append(" limit ").append(size*index).append(" , ").append(size);
			}
		}
        else {
        	sb.append(" limit ").append(0).append(" , ").append(size);
		}
        return sb.toString();
    }
    
    /**
	 * 拦截类型StatementHandler 
	 */
	@Override
	public Object plugin(Object target) {
	    if (target instanceof StatementHandler) {  
	        return Plugin.wrap(target, this);  
	    } else {  
	        return target;  
	    }  
	}

	@Override
	public void setProperties(Properties properties) {
	    return;
	}

	/** 
	 * 对SQL参数(?)设值 
	 * @param ps 
	 * @param mappedStatement 
	 * @param boundSql 
	 * @param parameterObject 
	 * @throws SQLException 
	 */  
	private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,  
	        Object parameterObject) throws SQLException {  
	    ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);  
	    parameterHandler.setParameters(ps);  
	}  

}
