/*
 * Copyright 2014 A.C.R. Development
 */
package acr.browser.lightning.database.history;

import android.app.Application;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;

import com.anthonycr.bonsai.Completable;
import com.anthonycr.bonsai.CompletableAction;
import com.anthonycr.bonsai.CompletableSubscriber;
import com.anthonycr.bonsai.Single;
import com.anthonycr.bonsai.SingleAction;
import com.anthonycr.bonsai.SingleSubscriber;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Singleton;

import acr.browser.lightning.R;
import acr.browser.lightning.database.HistoryItem;


/**
 * The disk backed download database.
 * See {@link HistoryModel} for method
 * documentation.
 */
@Singleton
@WorkerThread
public class HistoryDatabase extends SQLiteOpenHelper implements HistoryModel {

    // All Static variables
    // Database Version
    private static final int DATABASE_VERSION = 2;

    // Database Name
    private static final String DATABASE_NAME = "historyManager";

    // HistoryItems table name
    private static final String TABLE_HISTORY = "history";

    // HistoryItems Table Columns names
    private static final String KEY_ID = "id";
    private static final String KEY_URL = "url";
    private static final String KEY_TITLE = "title";
    private static final String KEY_TIME_VISITED = "time";

    @Nullable private SQLiteDatabase mDatabase;

    @Inject
    public HistoryDatabase(@NonNull Application application) {
        super(application, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Creating Tables
    @Override
    public void onCreate(@NonNull SQLiteDatabase db) {
        String CREATE_HISTORY_TABLE = "CREATE TABLE " + TABLE_HISTORY + '(' + KEY_ID
            + " INTEGER PRIMARY KEY," + KEY_URL + " TEXT," + KEY_TITLE + " TEXT,"
            + KEY_TIME_VISITED + " INTEGER" + ')';
        db.execSQL(CREATE_HISTORY_TABLE);
    }

    // Upgrading database
    @Override
    public void onUpgrade(@NonNull SQLiteDatabase db, int oldVersion, int newVersion) {
        // Drop older table if it exists
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY);
        // Create tables again
        onCreate(db);
    }

    @NonNull
    private static HistoryItem fromCursor(@NonNull Cursor cursor) {
        HistoryItem historyItem = new HistoryItem();
        historyItem.setUrl(cursor.getString(1));
        historyItem.setTitle(cursor.getString(2));
        historyItem.setImageId(R.drawable.ic_history);

        return historyItem;
    }

    @WorkerThread
    @NonNull
    private synchronized SQLiteDatabase lazyDatabase() {
        if (mDatabase == null || !mDatabase.isOpen()) {
            mDatabase = this.getWritableDatabase();
        }
        return mDatabase;
    }

    @NonNull
    @Override
    public Completable deleteHistory() {
        return Completable.create(new CompletableAction() {
            @Override
            public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
                lazyDatabase().delete(TABLE_HISTORY, null, null);
                lazyDatabase().close();

                subscriber.onComplete();
            }
        });
    }

    @NonNull
    @Override
    public Completable deleteHistoryItem(@NonNull final String url) {
        return Completable.create(new CompletableAction() {
            @Override
            public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
                lazyDatabase().delete(TABLE_HISTORY, KEY_URL + " = ?", new String[]{url});

                subscriber.onComplete();
            }
        });
    }

    @NonNull
    @Override
    public Completable visitHistoryItem(@NonNull final String url, @Nullable final String title) {
        return Completable.create(new CompletableAction() {
            @Override
            public void onSubscribe(@NonNull CompletableSubscriber subscriber) {
                ContentValues values = new ContentValues();
                values.put(KEY_TITLE, title == null ? "" : title);
                values.put(KEY_TIME_VISITED, System.currentTimeMillis());

                Cursor cursor = lazyDatabase().query(false, TABLE_HISTORY, new String[]{KEY_URL},
                    KEY_URL + " = ?", new String[]{url}, null, null, null, "1");

                if (cursor.getCount() > 0) {
                    lazyDatabase().update(TABLE_HISTORY, values, KEY_URL + " = ?", new String[]{url});
                } else {
                    addHistoryItem(new HistoryItem(url, title == null ? "" : title));
                }

                cursor.close();
            }
        });
    }

    @NonNull
    @Override
    public Single<List<HistoryItem>> findHistoryItemsContaining(@NonNull final String query) {
        return Single.create(new SingleAction<List<HistoryItem>>() {
            @Override
            public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
                List<HistoryItem> itemList = new ArrayList<>(5);

                String search = '%' + query + '%';

                Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, KEY_TITLE + " LIKE ? OR " + KEY_URL + " LIKE ?",
                    new String[]{search, search}, null, null, KEY_TIME_VISITED + " DESC", "5");

                while (cursor.moveToNext()) {
                    itemList.add(fromCursor(cursor));
                }

                cursor.close();

                subscriber.onItem(itemList);
                subscriber.onComplete();
            }
        });
    }

    @NonNull
    @Override
    public Single<List<HistoryItem>> lastHundredVisitedHistoryItems() {
        return Single.create(new SingleAction<List<HistoryItem>>() {
            @Override
            public void onSubscribe(@NonNull SingleSubscriber<List<HistoryItem>> subscriber) {
                List<HistoryItem> itemList = new ArrayList<>(100);
                Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, null, null, null, null, KEY_TIME_VISITED + " DESC", "100");

                while (cursor.moveToNext()) {
                    itemList.add(fromCursor(cursor));
                }

                cursor.close();

                subscriber.onItem(itemList);
                subscriber.onComplete();
            }
        });
    }

    @WorkerThread
    private synchronized void addHistoryItem(@NonNull HistoryItem item) {
        ContentValues values = new ContentValues();
        values.put(KEY_URL, item.getUrl());
        values.put(KEY_TITLE, item.getTitle());
        values.put(KEY_TIME_VISITED, System.currentTimeMillis());
        lazyDatabase().insert(TABLE_HISTORY, null, values);
    }

    @WorkerThread
    @Nullable
    synchronized String getHistoryItem(@NonNull String url) {
        Cursor cursor = lazyDatabase().query(TABLE_HISTORY, new String[]{KEY_ID, KEY_URL, KEY_TITLE},
            KEY_URL + " = ?", new String[]{url}, null, null, null, "1");
        String m = null;
        if (cursor != null) {
            cursor.moveToFirst();
            m = cursor.getString(0);

            cursor.close();
        }
        return m;
    }

    @WorkerThread
    @NonNull
    synchronized List<HistoryItem> getAllHistoryItems() {
        List<HistoryItem> itemList = new ArrayList<>();

        Cursor cursor = lazyDatabase().query(TABLE_HISTORY, null, null, null, null, null, KEY_TIME_VISITED + " DESC");

        while (cursor.moveToNext()) {
            itemList.add(fromCursor(cursor));
        }

        cursor.close();

        return itemList;
    }

    @WorkerThread
    synchronized long getHistoryItemsCount() {
        return DatabaseUtils.queryNumEntries(mDatabase, TABLE_HISTORY);
    }
}