package com.pushtorefresh.storio.db.impl;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;

import com.pushtorefresh.storio.db.StorIODb;
import com.pushtorefresh.storio.db.operation.Changes;
import com.pushtorefresh.storio.db.operation.delete.PreparedDelete;
import com.pushtorefresh.storio.db.operation.exec_sql.PreparedExecSql;
import com.pushtorefresh.storio.db.operation.get.PreparedGet;
import com.pushtorefresh.storio.db.operation.put.PreparedPut;
import com.pushtorefresh.storio.db.query.DeleteQuery;
import com.pushtorefresh.storio.db.query.InsertQuery;
import com.pushtorefresh.storio.db.query.Query;
import com.pushtorefresh.storio.db.query.RawQuery;
import com.pushtorefresh.storio.db.query.UpdateQuery;

import java.util.Set;

import rx.Observable;
import rx.functions.Func1;
import rx.subjects.PublishSubject;

public class StorIOSQLiteDb extends StorIODb {

    /**
     * Real db
     */
    @NonNull private final SQLiteDatabase db;

    /**
     * Reactive bus for notifying observers about changes in StorIODb
     * One change can affect several tables, so we use Set<String> as set of changed tables per event
     */
    @NonNull private final PublishSubject<Changes> changesBus = PublishSubject.create();

    /**
     * Implementation of {@link StorIODb.Internal}
     */
    @NonNull private final Internal internal = new InternalImpl();

    protected StorIOSQLiteDb(@NonNull SQLiteDatabase db) {
        this.db = db;
    }

    @NonNull @Override public PreparedExecSql.Builder execSql() {
        return new PreparedExecSql.Builder(this);
    }

    @NonNull @Override public PreparedGet.Builder get() {
        return new PreparedGet.Builder(this);
    }

    @NonNull @Override public PreparedPut.Builder put() {
        return new PreparedPut.Builder(this);
    }

    @NonNull @Override public PreparedDelete.Builder delete() {
        return new PreparedDelete.Builder(this);
    }

    @Override @NonNull
    public Observable<Changes> observeChangesInTables(@NonNull final Set<String> tables) {
        return changesBus
                .filter(new Func1<Changes, Boolean>() {
                    @Override public Boolean call(Changes changes) {
                        // if one of changed tables found in tables for subscription -> notify observer
                        for (String changedTable : changes.tables()) {
                            if (tables.contains(changedTable)) {
                                return true;
                            }
                        }

                        return false;
                    }
                });
    }

    @NonNull @Override public Internal internal() {
        return internal;
    }

    protected class InternalImpl extends Internal {

        @Override public void execSql(@NonNull RawQuery rawQuery) {
            db.execSQL(rawQuery.query, rawQuery.args);
        }

        @NonNull @Override public Cursor rawQuery(@NonNull RawQuery rawQuery) {
            return db.rawQuery(
                    rawQuery.query,
                    rawQuery.args
            );
        }

        @NonNull @Override public Cursor query(@NonNull Query query) {
            return db.query(
                    query.distinct,
                    query.table,
                    query.columns,
                    query.where,
                    query.whereArgs,
                    query.groupBy,
                    query.having,
                    query.orderBy,
                    query.limit
            );
        }

        @Override
        public long insert(@NonNull InsertQuery insertQuery, @NonNull ContentValues contentValues) {
            return db.insertOrThrow(
                    insertQuery.table,
                    insertQuery.nullColumnHack,
                    contentValues
            );
        }

        @Override
        public int update(@NonNull UpdateQuery updateQuery, @NonNull ContentValues contentValues) {
            return db.update(
                    updateQuery.table,
                    contentValues,
                    updateQuery.where,
                    updateQuery.whereArgs
            );
        }

        @Override public int delete(@NonNull DeleteQuery deleteQuery) {
            return db.delete(
                    deleteQuery.table,
                    deleteQuery.where,
                    deleteQuery.whereArgs
            );
        }

        @Override public void notifyAboutChanges(@NonNull Changes changes) {
            changesBus.onNext(changes);
        }

        @Override public boolean transactionsSupported() {
            return true;
        }

        @Override public void beginTransaction() {
            db.beginTransaction();
        }

        @Override public void setTransactionSuccessful() {
            db.setTransactionSuccessful();
        }

        @Override public void endTransaction() {
            db.endTransaction();
        }
    }

    public static class Builder {

        private SQLiteDatabase db;

        @NonNull public Builder db(@NonNull SQLiteDatabase db) {
            this.db = db;
            return this;
        }

        @NonNull public Builder sqliteOpenHelper(@NonNull SQLiteOpenHelper sqliteOpenHelper) {
            db = sqliteOpenHelper.getWritableDatabase();
            return this;
        }

        @NonNull public StorIOSQLiteDb build() {
            if (db == null) {
                throw new IllegalStateException("Please specify SQLiteDatabase instance");
            }

            return new StorIOSQLiteDb(db);
        }
    }
}

@@ -349,271 +349,8 @@
 es;%0A
-import com.pushtorefresh.storio.db.operation.delete.PreparedDelete;%0Aimport com.pushtorefresh.storio.db.operation.exec_sql.PreparedExecSql;%0Aimport com.pushtorefresh.storio.db.operation.get.PreparedGet;%0Aimport com.pushtorefresh.storio.db.operation.put.PreparedPut;%0A
 impo
@@ -1346,473 +1346,8 @@
  %7D%0A%0A
-    @NonNull @Override public PreparedExecSql.Builder execSql() %7B%0A        return new PreparedExecSql.Builder(this);%0A    %7D%0A%0A    @NonNull @Override public PreparedGet.Builder get() %7B%0A        return new PreparedGet.Builder(this);%0A    %7D%0A%0A    @NonNull @Override public PreparedPut.Builder put() %7B%0A        return new PreparedPut.Builder(this);%0A    %7D%0A%0A    @NonNull @Override public PreparedDelete.Builder delete() %7B%0A        return new PreparedDelete.Builder(this);%0A    %7D%0A%0A
     

Remove unneeded overrides from StorIOSQLiteDb