package com.pictc.jdbc;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URL;
import java.sql.Array;
import java.sql.Date;
import java.sql.NClob;
import java.sql.Ref;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowId;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.SQLXML;
import java.sql.Struct;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.format.DateTimeParseException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.pictc.utils.StringUtils;


public class ResultSetUtils {
	
	private static Pattern linePattern = Pattern.compile("_(\\w)");
	
	@SuppressWarnings("unchecked")
	public static <T> T getObject(ResultSet res,int columnIndex,Class<T> type) throws SQLException {
		if (type.equals(String.class)) {
            return (T) res.getString(columnIndex);

        } else if (type.equals(BigDecimal.class)) {
            return (T) res.getBigDecimal(columnIndex);

        } else if (type.equals(BigInteger.class)) {
            return (T) getBigInteger(res, columnIndex);

        } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
            return (T) Boolean.valueOf(res.getBoolean(columnIndex));

        } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
            return (T) Integer.valueOf(res.getInt(columnIndex));

        } else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
            return (T) Long.valueOf(res.getLong(columnIndex));
        } else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
            return (T) Float.valueOf(res.getFloat(columnIndex));

        } else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
            return (T) Double.valueOf(res.getDouble(columnIndex));

        } else if (type.equals(byte[].class)) {
            return (T) res.getBytes(columnIndex);

        } else if (type.equals(Date.class)) {
            return (T) res.getDate(columnIndex);

        } else if (type.equals(Time.class)) {
            return (T) res.getTime(columnIndex);

        } else if (type.equals(Timestamp.class)) {
            return (T) res.getTimestamp(columnIndex);

        } else if (type.equals(java.sql.Clob.class)) {
            return (T) res.getClob(columnIndex);

        } else if (type.equals(java.sql.Blob.class)) {
            return (T) res.getBlob(columnIndex);

        } else if (type.equals(Array.class)) {
            return (T) res.getArray(columnIndex);

        } else if (type.equals(Ref.class)) {
            return (T) res.getRef(columnIndex);

        } else if (type.equals(URL.class)) {
            return (T) res.getURL(columnIndex);

        } else if (type.equals(Struct.class)) {
            throw new SQLFeatureNotSupportedException();

        } else if (type.equals(RowId.class)) {
            return (T) res.getRowId(columnIndex);

        } else if (type.equals(NClob.class)) {
            return (T) res.getNClob(columnIndex);

        } else if (type.equals(SQLXML.class)) {
            return (T) res.getSQLXML(columnIndex);

        } else if (type.equals(LocalDate.class)) {
            return (T) getLocalDate(res,columnIndex);

        } else if (type.equals(LocalDateTime.class)) {
            return (T) getLocalDateTime(res,columnIndex);

        } else if (type.equals(LocalTime.class)) {
            return (T) getLocalTime(res,columnIndex);

        } else if (type.equals(OffsetDateTime.class)) {
            try {
                String odt = res.getString(columnIndex);
                return odt == null ? null : (T) OffsetDateTime.parse(odt);
            } catch (DateTimeParseException e) {
                // Let it continue and try by object deserialization.
            }

        } else if (type.equals(OffsetTime.class)) {
            try {
                String ot = res.getString(columnIndex);
                return ot == null ? null : (T) OffsetTime.parse(ot);
            } catch (DateTimeParseException e) {}
        }else if(type.isEnum()) {
			try {
				String _vl = res.getString(columnIndex);
				if(!StringUtils.isEmpty(_vl)) {
					Method method=Enum.class.getMethod("valueOf", String.class);
					return (T) method.invoke(null,_vl);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
        }
		return (T) res.getObject(columnIndex);
	}
	
    private static LocalDate getLocalDate(ResultSet res,int columnIndex) throws SQLException {
    	Date date = res.getDate(columnIndex);
    	if(date!=null) {
    		return date.toLocalDate();
    	}
        return null;
    }
    
    private static LocalDateTime getLocalDateTime(ResultSet res,int columnIndex) throws SQLException {
    	Timestamp date = res.getTimestamp(columnIndex);
    	if(date!=null) {
    		return date.toLocalDateTime();
    	}
    	return null;
    }
    
    private static LocalTime getLocalTime(ResultSet res,int columnIndex) throws SQLException {
    	Time date = res.getTime(columnIndex);
    	if(date!=null) {
    		return date.toLocalTime();
    	}
    	return null;
    }
    
    private static BigInteger getBigInteger(ResultSet res,int columnIndex) throws SQLException {
        String stringVal = res.getString(columnIndex);
        if (stringVal == null) {
            return null;
        }
        try {
            return new BigInteger(stringVal);
        } catch (NumberFormatException nfe) {
        	throw new RuntimeException("ResultSet.Bad_format_for_BigInteger");
        }
    }
    
    
    @SuppressWarnings("unchecked")
	public static <T> T getObject(ResultSet res,String name,Class<T> type) throws SQLException {
		if (type.equals(String.class)) {
            return (T) res.getString(name);

        } else if (type.equals(BigDecimal.class)) {
            return (T) res.getBigDecimal(name);

        } else if (type.equals(BigInteger.class)) {
            return (T) getBigInteger(res, name);

        } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
            return (T) Boolean.valueOf(res.getBoolean(name));

        } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
            return (T) Integer.valueOf(res.getInt(name));

        } else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
            return (T) Long.valueOf(res.getLong(name));

        } else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
            return (T) Float.valueOf(res.getFloat(name));

        } else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
            return (T) Double.valueOf(res.getDouble(name));

        } else if (type.equals(byte[].class)) {
            return (T) res.getBytes(name);

        } else if (type.equals(Date.class)) {
            return (T) res.getDate(name);

        } else if (type.equals(Time.class)) {
            return (T) res.getTime(name);

        } else if (type.equals(java.util.Date.class) || type.equals(Timestamp.class)) {
            return (T) res.getTimestamp(name);

        } else if (type.equals(java.sql.Clob.class)) {
            return (T) res.getClob(name);

        } else if (type.equals(java.sql.Blob.class)) {
            return (T) res.getBlob(name);

        } else if (type.equals(Array.class)) {
            return (T) res.getArray(name);

        } else if (type.equals(Ref.class)) {
            return (T) res.getRef(name);

        } else if (type.equals(URL.class)) {
            return (T) res.getURL(name);

        } else if (type.equals(Struct.class)) {
            throw new SQLFeatureNotSupportedException();

        } else if (type.equals(RowId.class)) {
            return (T) res.getRowId(name);

        } else if (type.equals(NClob.class)) {
            return (T) res.getNClob(name);

        } else if (type.equals(SQLXML.class)) {
            return (T) res.getSQLXML(name);

        } else if (type.equals(LocalDate.class)) {
            return (T) getLocalDate(res,name);

        } else if (type.equals(LocalDateTime.class)) {
            return (T) getLocalDateTime(res,name);

        } else if (type.equals(LocalTime.class)) {
            return (T) getLocalTime(res,name);

        } else if (type.equals(OffsetDateTime.class)) {
            try {
                String odt = res.getString(name);
                return odt == null ? null : (T) OffsetDateTime.parse(odt);
            } catch (DateTimeParseException e) {
                // Let it continue and try by object deserialization.
            }

        } else if (type.equals(OffsetTime.class)) {
            try {
                String ot = res.getString(name);
                return ot == null ? null : (T) OffsetTime.parse(ot);
            } catch (DateTimeParseException e) {}
        }else if(type.isEnum()) {
			try {
				String _vl = res.getString(name);
				if(!StringUtils.isEmpty(_vl)) {
					Method method=Enum.class.getMethod("valueOf", String.class);
					return (T) method.invoke(null,_vl);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
        }
		return (T) res.getObject(name);
	}
	
    private static LocalDate getLocalDate(ResultSet res,String name) throws SQLException {
    	Date date = res.getDate(name);
    	if(date!=null) {
    		return date.toLocalDate();
    	}
        return null;
    }
    
    private static LocalDateTime getLocalDateTime(ResultSet res,String name) throws SQLException {
    	Timestamp date = res.getTimestamp(name);
    	if(date!=null) {
    		return date.toLocalDateTime();
    	}
    	return null;
    }
    
    private static LocalTime getLocalTime(ResultSet res,String name) throws SQLException {
    	Time date = res.getTime(name);
    	if(date!=null) {
    		return date.toLocalTime();
    	}
    	return null;
    }
    
    private static BigInteger getBigInteger(ResultSet res,String name) throws SQLException {
        String stringVal = res.getString(name);
        if (stringVal == null) {
            return null;
        }
        try {
            return new BigInteger(stringVal);
        } catch (NumberFormatException nfe) {
        	throw new RuntimeException("ResultSet.Bad_format_for_BigInteger");
        }
    }
    
    
    public static <T>T getSingleValue(ResultSet res,Class<T> klass) throws SQLException{
    	if(res.next()) {
    		return getObject(res,1, klass);
    	}
		return null;
    }
    
	/**
	 * 获取结果集的标题名称
	 * */
    public static Map<String,String> getMapColumns(ResultSet set) throws SQLException{
		ResultSetMetaData meta=set.getMetaData();
		Map<String,String> result=new HashMap<>();
		for (int i = 1; i <= meta.getColumnCount(); i++) {
			result.put(meta.getColumnLabel(i), parseKey(meta.getColumnLabel(i)));
		}
		return result;
	}
    
	/**
	 * 获取结果集的标题名称
	 * */
    public static Set<String> getResultColumns(ResultSet set) throws SQLException{
		ResultSetMetaData meta=set.getMetaData();
		Set<String> result=new HashSet<>(meta.getColumnCount());
		for (int i = 1; i <= meta.getColumnCount(); i++) {
			result.add(meta.getColumnLabel(i));
		}
		return result;
	}
    
    public static String parseKey(String str) {
		str = str.toLowerCase();
		Matcher matcher = linePattern.matcher(str);
		StringBuffer sb = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
		}
		matcher.appendTail(sb);
		return sb.toString();
	}

}


