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.CallableStatement;
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 com.pictc.utils.StringUtils;

public class CallUtils {
	
	@SuppressWarnings("unchecked")
	public static <T> T getObject(CallableStatement 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(CallableStatement res,int columnIndex) throws SQLException {
    	Date date = res.getDate(columnIndex);
    	if(date!=null) {
    		return date.toLocalDate();
    	}
        return null;
    }
    
    private static LocalDateTime getLocalDateTime(CallableStatement res,int columnIndex) throws SQLException {
    	Timestamp date = res.getTimestamp(columnIndex);
    	if(date!=null) {
    		return date.toLocalDateTime();
    	}
    	return null;
    }
    
    private static LocalTime getLocalTime(CallableStatement res,int columnIndex) throws SQLException {
    	Time date = res.getTime(columnIndex);
    	if(date!=null) {
    		return date.toLocalTime();
    	}
    	return null;
    }
    
    private static BigInteger getBigInteger(CallableStatement 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("CallableStatement.Bad_format_for_BigInteger");
        }
    }

}
