package com.sobot.network.http.db;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import android.util.Pair;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;

/**
 * dao类base
 */
public abstract class SobotBaseDao<T> {
    private static final boolean isDebug = false;
    protected static String TAG;
    protected Lock lock;
    protected SQLiteOpenHelper helper;
    protected SQLiteDatabase database;

    public SobotBaseDao(SQLiteOpenHelper helper) {
        TAG = getClass().getSimpleName();
        lock = SobotDBHelper.lock;
        this.helper = helper;
        this.database = openWriter();
    }

    public SQLiteDatabase openWriter() {
        try {
            return helper.getWritableDatabase();
        } catch (Exception e) {
            //ignore
        }
        return null;
    }

    protected final void closeDatabase(SQLiteDatabase database, Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) cursor.close();
        if (database != null && database.isOpen()) database.close();
    }

    /** 插入一条记录 */
    public boolean insert(T t) {
        if (t == null) return false;
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.insert(getTableName(), null, getContentValues(t));
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " insertT");
            }
        }
        return false;
    }

    /** 插入一条记录 */
    public long insert(SQLiteDatabase database, T t) {
        return database.insert(getTableName(), null, getContentValues(t));
    }

    /** 插入多条记录 */
    public boolean insert(List<T> ts) {
        if (ts == null) return false;
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (T t : ts) {
                database.insert(getTableName(), null, getContentValues(t));
            }
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " insertList");
            }
        }
        return false;
    }

    public boolean insert(SQLiteDatabase database, List<T> ts) {
        try {
            for (T t : ts) {
                database.insert(getTableName(), null, getContentValues(t));
            }
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
            return false;
        }
    }

    /** 删除所有数据 */
    public boolean deleteAll() {
        return delete(null, null);
    }

    /** 删除所有数据 */
    public long deleteAll(SQLiteDatabase database) {
        return delete(database, null, null);
    }

    /** 根据条件删除数据库中的数据 */
    public boolean delete(String whereClause, String[] whereArgs) {
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.delete(getTableName(), whereClause, whereArgs);
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " delete");
            }
        }
        return false;
    }

    /** 根据条件删除数据库中的数据 */
    public long delete(SQLiteDatabase database, String whereClause, String[] whereArgs) {
        return database.delete(getTableName(), whereClause, whereArgs);
    }

    public boolean deleteList(List<Pair<String, String[]>> where) {
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (Pair<String, String[]> pair : where) {
                database.delete(getTableName(), pair.first, pair.second);
            }
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " deleteList");
            }
        }
        return false;
    }

    /**
     * replace 语句有如下行为特点
     * 1. replace语句会删除原有的一条记录， 并且插入一条新的记录来替换原记录。
     * 2. 一般用replace语句替换一条记录的所有列， 如果在replace语句中没有指定某列， 在replace之后这列的值被置空 。
     * 3. replace语句根据主键的值确定被替换的是哪一条记录
     * 4. 如果执行replace语句时， 不存在要替换的记录， 那么就会插入一条新的记录。
     * 5. replace语句不能根据where子句来定位要被替换的记录
     * 6. 如果新插入的或替换的记录中， 有字段和表中的其他记录冲突， 那么会删除那条其他记录。
     */
    public boolean replace(T t) {
        if (t == null) return false;
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.replace(getTableName(), null, getContentValues(t));
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " replaceT");
            }
        }
        return false;
    }

    public long replace(SQLiteDatabase database, T t) {
        return database.replace(getTableName(), null, getContentValues(t));
    }

    public boolean replace(ContentValues contentValues) {
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.replace(getTableName(), null, contentValues);
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " replaceContentValues");
            }
        }
        return false;
    }

    public long replace(SQLiteDatabase database, ContentValues contentValues) {
        return database.replace(getTableName(), null, contentValues);
    }

    public boolean replace(List<T> ts) {
        if (ts == null) return false;
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            for (T t : ts) {
                database.replace(getTableName(), null, getContentValues(t));
            }
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " replaceList");
            }
        }
        return false;
    }

    public boolean replace(SQLiteDatabase database, List<T> ts) {
        try {
            for (T t : ts) {
                database.replace(getTableName(), null, getContentValues(t));
            }
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
            return false;
        }
    }

    /** 更新一条记录 */
    public boolean update(T t, String whereClause, String[] whereArgs) {
        if (t == null) return false;
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.update(getTableName(), getContentValues(t), whereClause, whereArgs);
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " updateT");
            }
        }
        return false;
    }

    /** 更新一条记录 */
    public long update(SQLiteDatabase database, T t, String whereClause, String[] whereArgs) {
        return database.update(getTableName(), getContentValues(t), whereClause, whereArgs);
    }

    /** 更新一条记录 */
    public boolean update(ContentValues contentValues, String whereClause, String[] whereArgs) {
        if (database == null) return false;
        long start = System.currentTimeMillis();
        lock.lock();
        try {
            database.beginTransaction();
            database.update(getTableName(), contentValues, whereClause, whereArgs);
            database.setTransactionSuccessful();
            return true;
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " updateContentValues");
            }
        }
        return false;
    }

    /** 更新一条记录 */
    public long update(SQLiteDatabase database, ContentValues contentValues, String whereClause, String[] whereArgs) {
        return database.update(getTableName(), contentValues, whereClause, whereArgs);
    }

    /** 查询并返回所有对象的集合 */
    public List<T> queryAll(SQLiteDatabase database) {
        return query(database, null, null);
    }

    /** 按条件查询对象并返回集合 */
    public List<T> query(SQLiteDatabase database, String selection, String[] selectionArgs) {
        return query(database, null, selection, selectionArgs, null, null, null, null);
    }

    /** 查询满足条件的一个结果 */
    public T queryOne(SQLiteDatabase database, String selection, String[] selectionArgs) {
        List<T> query = query(database, null, selection, selectionArgs, null, null, null, "1");
        if (query.size() > 0) return query.get(0);
        return null;
    }

    /** 按条件查询对象并返回集合 */
    public List<T> query(SQLiteDatabase database, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
        List<T> list = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = database.query(getTableName(), columns, selection, selectionArgs, groupBy, having, orderBy, limit);
            while (!cursor.isClosed() && cursor.moveToNext()) {
                list.add(parseCursorToBean(cursor));
            }
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            closeDatabase(null, cursor);
        }
        return list;
    }

    /** 查询并返回所有对象的集合 */
    public List<T> queryAll() {
        return query(null, null);
    }

    /** 按条件查询对象并返回集合 */
    public List<T> query(String selection, String[] selectionArgs) {
        return query(null, selection, selectionArgs, null, null, null, null);
    }

    /** 查询满足条件的一个结果 */
    public T queryOne(String selection, String[] selectionArgs) {
        long start = System.currentTimeMillis();
        List<T> query = query(null, selection, selectionArgs, null, null, null, "1");
        if (isDebug) {
            Log.v(TAG, System.currentTimeMillis() - start + " queryOne");
        }
        return query.size() > 0 ? query.get(0) : null;
    }

    /** 按条件查询对象并返回集合 */
    public List<T> query(String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {
        if (database == null) return new ArrayList<>();
        long start = System.currentTimeMillis();
        lock.lock();
        List<T> list = new ArrayList<>();
        Cursor cursor = null;
        try {
            database.beginTransaction();
            cursor = database.query(getTableName(), columns, selection, selectionArgs, groupBy, having, orderBy, limit);
            while (!cursor.isClosed() && cursor.moveToNext()) {
                list.add(parseCursorToBean(cursor));
            }
            database.setTransactionSuccessful();
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            closeDatabase(null, cursor);
            database.endTransaction();
            lock.unlock();
            if (isDebug) {
                Log.v(TAG, System.currentTimeMillis() - start + " query");
            }
        }
        return list;
    }

    public interface Action {
        void call(SQLiteDatabase database);
    }

    /** 用于给外界提供事物开启的模板 */
    public void startTransaction(Action action) {
        if (database == null) return;
        lock.lock();
        try {
            database.beginTransaction();
            action.call(database);
            database.setTransactionSuccessful();
        } catch (Exception e) {
            if (isDebug) {
                Log.e(TAG,"",e);
            }
        } finally {
            database.endTransaction();
            lock.unlock();
        }
    }

    /** 获取对应的表名 */
    public abstract String getTableName();

    public abstract void unInit();

    /** 将Cursor解析成对应的JavaBean */
    public abstract T parseCursorToBean(Cursor cursor);

    /** 需要替换的列 */
    public abstract ContentValues getContentValues(T t);
}
