package com.mq.mylibrary.tool;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 创建人 mQ
 * 创建时间 2019/5/20 0020 10:33
 * 文件名称 HappySQL
 * 说明: sql查询结果转实体,转集合
 **/
public class HappySQL {

    /**
     * 通过SQL语句获得对应的VO。注意：Cursor的字段名或者别名一定要和VO的成员名一样
     *
     * @param db
     * @param sql
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static Object sql2VO(SQLiteDatabase db, String sql, Class clazz) {
        Cursor c = db.rawQuery(sql, null);
        return cursor2VO(c, clazz);
    }

    /**
     * 通过SQL语句获得对应的VO。注意：Cursor的字段名或者别名一定要和VO的成员名一样
     *
     * @param db
     * @param sql
     * @param selectionArgs
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static Object sql2VO(SQLiteDatabase db, String sql,
                                String[] selectionArgs, Class clazz) {
        Cursor c = db.rawQuery(sql, selectionArgs);
        return cursor2VO(c, clazz);
    }

    /**
     * 通过SQL语句获得对应的VO的List。注意：Cursor的字段名或者别名一定要和VO的成员名一样
     *
     * @param db
     * @param sql
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static List sql2VOList(SQLiteDatabase db, String sql, Class clazz) {
        Cursor c = db.rawQuery(sql, null);
        return cursor2VOList(c, clazz);
    }

    /**
     * 通过SQL语句获得对应的VO的List。注意：Cursor的字段名或者别名一定要和VO的成员名一样
     *
     * @param db
     * @param sql
     * @param selectionArgs
     * @param clazz
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static List sql2VOList(SQLiteDatabase db, String sql,
                                  String[] selectionArgs, Class clazz) {
        Cursor c = db.rawQuery(sql, selectionArgs);
        return cursor2VOList(c, clazz);
    }

    /**
     * 通过Cursor转换成对应的VO。注意：Cursor里的字段名（可用别名）必须要和VO的属性名一致
     *
     * @param c
     * @param clazz
     * @return
     */
    @SuppressWarnings({"rawtypes", "unused"})
    public static Object cursor2VO(Cursor c, Class clazz) {
        if (c == null) {
            return null;
        }
        Object obj;
        int i = 1;
        try {
            c.moveToNext();
            obj = setValuesToClass(c, clazz);

            return obj;
        } catch (Exception e) {
            System.out.println(e);
            System.out.println("ERROR @：cursor2VO");
            return null;
        } finally {
            c.close();
        }
    }

    /**
     * 通过Cursor转换成对应的VO集合。注意：Cursor里的字段名（可用别名）必须要和VO的属性名一致
     *
     * @param c
     * @param clazz
     * @return
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static List cursor2VOList(Cursor c, Class clazz) {
        if (c == null) {
            return null;
        }
        List list = new LinkedList();
        Object obj;
        try {
            while (c.moveToNext()) {
                obj = setValuesToClass(c, clazz);
                list.add(obj);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("ERROR @：cursor2VOList");
            return null;
        } finally {
            c.close();
        }
    }

    /**
     * 把值设置进类属性里
     *
     * @param c
     * @throws Exception
     */
    @SuppressWarnings("rawtypes")
    private static Object setValues2Fields(Cursor c, Class clazz)
            throws Exception {
        String[] columnNames = c.getColumnNames();// 字段数组
        Object obj = clazz.newInstance();
        Field[] fields = clazz.getFields();

        for (Field _field : fields) {
            Class<? extends Object> typeClass = _field.getType();// 属性类型
            for (int j = 0; j < columnNames.length; j++) {
                String columnName = columnNames[j];
                typeClass = getBasicClass(typeClass);
                boolean isBasicType = isBasicType(typeClass);

                if (isBasicType) {
                    if (columnName.equalsIgnoreCase(_field.getName())) {// 是基本类型
                        String _str = c.getString(c.getColumnIndex(columnName));
                        if (_str == null) {
                            break;
                        }
                        _str = _str == null ? "" : _str;
                        Constructor<? extends Object> cons = typeClass
                                .getConstructor(String.class);
                        Object attribute = cons.newInstance(_str);
                        _field.setAccessible(true);
                        _field.set(obj, attribute);
                        break;
                    }
                } else {
                    Object obj2 = setValues2Fields(c, typeClass);// 递归
                    _field.set(obj, obj2);
                    break;
                }

            }
        }
        return obj;
    }

    private static Object setValuesToClass(Cursor c, Class clazz) throws Exception {
        String[] columnNames = c.getColumnNames();//字段数组
        Object obj = clazz.newInstance();
        Field[] fields = clazz.getFields();

        for (Field _field : fields) {
            Class<?> type = _field.getType();
            for (int i = 0; i < columnNames.length; i++) {
                String columnName = columnNames[i];
                if (columnName.equalsIgnoreCase(_field.getName())) {

                    if (type == byte[].class) {
                        byte[] blob = c.getBlob(c.getColumnIndex(columnName));
                        _field.set(obj, blob);
                    } else if (type == String.class) {
                        String string = c.getString(c.getColumnIndex(columnName));
                        _field.set(obj, string);
                    } else if (type == Integer.class) {
                        int anInt = c.getInt(c.getColumnIndex(columnName));
                        _field.set(obj, anInt);
                    } else if (type == Double.class) {
                        double aDouble = c.getDouble(c.getColumnIndex(columnName));
                        _field.set(obj, aDouble);
                    } else if (type == Float.class) {
                        float aFloat = c.getFloat(c.getColumnIndex(columnName));
                        _field.set(obj, aFloat);
                    } else if (type == Long.class) {
                        long aLong = c.getLong(c.getColumnIndex(columnName));
                        _field.set(obj, aLong);
                    } else if (type == Short.class) {
                        short aShort = c.getShort(c.getColumnIndex(columnName));
                        _field.set(obj, aShort);
                    }
                }
            }
        }
        return obj;
    }


    /**
     * 判断是不是基本类型
     *
     * @param typeClass
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static boolean isBasicType(Class typeClass) {
        if (typeClass.equals(Integer.class) || typeClass.equals(Long.class)
                || typeClass.equals(Float.class)
                || typeClass.equals(Double.class)
                || typeClass.equals(Boolean.class)
                || typeClass.equals(Byte.class)
                || typeClass.equals(Short.class)
                || typeClass.equals(String.class)) {

            return true;

        } else {
            return false;
        }
    }

    /**
     * 获得包装类
     *
     * @param typeClass
     * @return
     */
    @SuppressWarnings("all")
    public static Class<? extends Object> getBasicClass(Class typeClass) {
        Class _class = basicMap.get(typeClass);
        if (_class == null)
            _class = typeClass;
        return _class;
    }

    @SuppressWarnings("rawtypes")
    private static Map<Class, Class> basicMap = new HashMap<Class, Class>();

    static {
        basicMap.put(int.class, Integer.class);
        basicMap.put(long.class, Long.class);
        basicMap.put(float.class, Float.class);
        basicMap.put(double.class, Double.class);
        basicMap.put(boolean.class, Boolean.class);
        basicMap.put(byte.class, Byte.class);
        basicMap.put(short.class, Short.class);
    }

}
