package com.github.yaa110.db;

import android.content.ContentValues;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import java.util.Hashtable;

/**
 * A wrapper to replicate the `SQLiteDatabase` class to manage a SQLite database with restoring capability.
 * This wrapper makes it possible to undo changes made after execution of SQL commands.
 */
@SuppressWarnings("UnusedDeclaration")
public class RestorableSQLiteDatabase {

    private static RestorableSQLiteDatabase mInstance = null;
    private SQLiteDatabase mSQLiteDatabase;
    private static final String TAG = "SQLiteDatabase";

    /**
     * The hash table to map the tag of a restoring statement to its query
     */
    public Hashtable<String, String> mTagQueryTable;

    public static RestorableSQLiteDatabase getInstance(SQLiteDatabase mSQLiteDatabase){
        if(mInstance == null)
        {
            mInstance = new RestorableSQLiteDatabase(mSQLiteDatabase);
        }
        return mInstance;
    }

    /**
     * Private constructor of singleton pattern
     * @param mSQLiteDatabase the instance of SQLiteDatabase to be wrapped
     */
    private RestorableSQLiteDatabase(SQLiteDatabase mSQLiteDatabase) {
        this.mSQLiteDatabase = mSQLiteDatabase;
    }

    /**
     * @return the wrapped SQLiteDatabase instance
     */
    public SQLiteDatabase getSQLiteDatabase() {
        return mSQLiteDatabase;
    }

    /**
     * Use the {@link android.database.sqlite.SQLiteDatabase#insert(String, String, android.content.ContentValues) insert} method.
     */
    public long insert(String table, String nullColumnHack, ContentValues values) {
        try {
            return insertWithOnConflict(table, nullColumnHack, values, SQLiteDatabase.CONFLICT_NONE);
        } catch (SQLException e) {
            Log.e(TAG, "Error inserting " + values, e);
            return -1;
        }
    }

    /**
     * Use the {@link android.database.sqlite.SQLiteDatabase#insertOrThrow(String, String, android.content.ContentValues) insertOrThrow} method.
     */
    public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
            throws SQLException {
        return insertWithOnConflict(table, nullColumnHack, values, SQLiteDatabase.CONFLICT_NONE);
    }

    /**
     * Use the {@link android.database.sqlite.SQLiteDatabase#replace(String, String, android.content.ContentValues) replace} method.
     */
    public long replace(String table, String nullColumnHack, ContentValues initialValues) {
        try {
            return insertWithOnConflict(table, nullColumnHack, initialValues,
                    SQLiteDatabase.CONFLICT_REPLACE);
        } catch (SQLException e) {
            Log.e(TAG, "Error inserting " + initialValues, e);
            return -1;
        }
    }

    /**
     * Use the {@link android.database.sqlite.SQLiteDatabase#replaceOrThrow(String, String, android.content.ContentValues) replaceOrThrow} method.
     */
    public long replaceOrThrow(String table, String nullColumnHack,
                               ContentValues initialValues) throws SQLException {
        return insertWithOnConflict(table, nullColumnHack, initialValues,
                SQLiteDatabase.CONFLICT_REPLACE);
    }

    /**
     * Use the {@link android.database.sqlite.SQLiteDatabase#replaceOrThrow(String, String, android.content.ContentValues) insertWithOnConflict} method.
     */
    public long insertWithOnConflict(String table, String nullColumnHack,
                                     ContentValues initialValues, int conflictAlgorithm) {
        return mSQLiteDatabase.insertWithOnConflict(
                table,
                nullColumnHack,
                initialValues,
                conflictAlgorithm
        );
    }

}

@@ -200,16 +200,38 @@
 shtable;
+%0Aimport java.util.Set;
 %0A%0A/**%0A *
@@ -712,20 +712,20 @@
 map 
-the
+a
  tag 
-of a
+to its
  res
@@ -735,25 +735,8 @@
 ing 
-statement to its 
 quer
@@ -916,24 +916,16 @@
 == null)
-%0A       
  %7B%0A     
@@ -1135,16 +1135,20 @@
 ance of 
+the 
 SQLiteDa
@@ -1243,24 +1243,68 @@
 Database) %7B%0A
+        mTagQueryTable = new Hashtable%3C%3E();%0A
         this
@@ -1365,137 +1365,1202 @@
   * 
-@return the wrapped SQLiteDatabase instance%0A     */%0A    public SQLiteDatabase getSQLiteDatabase() %7B%0A        return mSQLiteDatabas
+Provides the instance of wrapped SQLiteDatabase%0A     * @return the instance of wrapped SQLiteDatabase%0A     */%0A    public SQLiteDatabase getSQLiteDatabase() %7B%0A        return mSQLiteDatabase;%0A    %7D%0A%0A    /**%0A     * Checks if the hash table contains the tag%0A     * @param tag Possible tag of restoring query%0A     * @return true if the hash table contains the tag; false otherwise%0A     */%0A    public boolean containsTag(String tag) %7B%0A        return mTagQueryTable.containsKey(tag);%0A    %7D%0A%0A    /**%0A     * Provides a Set view of the tags contained in the hash table%0A     * @return a Set view of the tags contained in the hash table%0A     */%0A    public Set%3CString%3E tagSet() %7B%0A        return mTagQueryTable.keySet();%0A    %7D%0A%0A    /**%0A     * Provides the query to which the tag is mapped%0A     * @param tag Possible tag of restoring query%0A     * @return the query to which the tag is mapped, or null if the hash table contains no mapping for the tag%0A     */%0A    public String getQuery(String tag) %7B%0A        return mTagQueryTable.get(tag);%0A    %7D%0A%0A    /**%0A     * Returns the hash table%0A     * @return the hash table%0A     */%0A    public Hashtable%3CString, String%3E getTagQueryTable() %7B%0A        return mTagQueryTabl
 e;%0A 

Create the methods of hash table