package com.amaze.filemanager.database;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.widget.Toast;

import net.gnu.explorer.R;
import com.amaze.filemanager.exceptions.CryptException;
import com.amaze.filemanager.utils.SmbUtil;
import com.amaze.filemanager.utils.files.CryptUtil;

import java.io.File;
import java.util.ArrayList;

/**
 * Created by Vishal on 29-05-2017.
 * Class handles database with tables having list of various utilities like
 * history, hidden files, list paths, grid paths, bookmarks, smb entry
 *
 * Try to use these functions from a background thread
 */

public class UtilsHandler extends SQLiteOpenHelper {

    private Context context;

    private static final String DATABASE_NAME = "utilities.db";
    private static final int DATABASE_VERSION = 1;  // increment only when making change in schema

    private static final String TABLE_HISTORY = "history";
    private static final String TABLE_HIDDEN = "hidden";
    private static final String TABLE_LIST = "list";
    private static final String TABLE_GRID = "grid";
    private static final String TABLE_BOOKMARKS = "bookmarks";
    private static final String TABLE_SMB = "smb";

    private static final String COLUMN_ID = "_id";
    private static final String COLUMN_PATH = "path";
    private static final String COLUMN_NAME = "name";

    public UtilsHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String queryHistory = "CREATE TABLE IF NOT EXISTS " + TABLE_HISTORY + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_PATH + " TEXT"
                + ")";
        String queryHidden = "CREATE TABLE IF NOT EXISTS " + TABLE_HIDDEN + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_PATH + " TEXT"
                + ")";
        String queryList = "CREATE TABLE IF NOT EXISTS " + TABLE_LIST + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_PATH + " TEXT"
                + ")";
        String queryGrid = "CREATE TABLE IF NOT EXISTS " + TABLE_GRID + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_PATH + " TEXT"
                + ")";
        String queryBookmarks = "CREATE TABLE IF NOT EXISTS " + TABLE_BOOKMARKS + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_NAME + " TEXT,"
                + COLUMN_PATH + " TEXT"
                + ")";
        String querySmb = "CREATE TABLE IF NOT EXISTS " + TABLE_SMB + " ("
                + COLUMN_ID + " INTEGER PRIMARY KEY,"
                + COLUMN_NAME + " TEXT,"
                + COLUMN_PATH + " TEXT"
                + ")";

        db.execSQL(queryHistory);
        db.execSQL(queryHidden);
        db.execSQL(queryList);
        db.execSQL(queryGrid);
        db.execSQL(queryBookmarks);
        db.execSQL(querySmb);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_HIDDEN);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_LIST);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_GRID);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS);
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_SMB);

        onCreate(db);
    }

    private enum Operation {
        HISTORY,
        HIDDEN,
        LIST,
        GRID,
        BOOKMARKS,
        SMB
    }

    public void addCommonBookmarks() {
        String sd = Environment.getExternalStorageDirectory() + "/";

        String[] dirs = new String[] {
                sd + Environment.DIRECTORY_DCIM,
                sd + Environment.DIRECTORY_DOWNLOADS,
                sd + Environment.DIRECTORY_MOVIES,
                sd + Environment.DIRECTORY_MUSIC,
                sd + Environment.DIRECTORY_PICTURES
        };

        for (String dir : dirs) {

            addBookmark(new File(dir).getName(), dir);
        }
    }

    public void addHistory(String path) {
        setPath(Operation.HISTORY, path);
    }

    public void addHidden(String path) {
        setPath(Operation.HIDDEN, path);
    }

    public void addListView(String path) {
        setPath(Operation.LIST, path);
    }

    public void addGridView(String path) {
        setPath(Operation.GRID, path);
    }

    public void addBookmark(String name, String path) {
        setPath(Operation.BOOKMARKS, name, path);
    }

    public void addSmb(String name, String path) {
        setPath(Operation.SMB, name, path);
    }

    public ArrayList<String> getHistoryList() {
        return getPath(Operation.HISTORY);
    }

    public ArrayList<String> getHiddenList() {
        return getPath(Operation.HIDDEN);
    }

    public ArrayList<String> getListViewList() {
        return getPath(Operation.LIST);
    }

    public ArrayList<String> getGridViewList() {
        return getPath(Operation.GRID);
    }

    public ArrayList<String[]> getBookmarksList() {

        SQLiteDatabase sqLiteDatabase = getReadableDatabase();

        Cursor cursor = sqLiteDatabase.query(getTableForOperation(Operation.BOOKMARKS), null,
                null, null, null, null, null);
        cursor.moveToFirst();

        ArrayList<String[]> row = new ArrayList<>();
        try {

            while (cursor.moveToNext()) {
                row.add(new String[] {
                        cursor.getString(cursor.getColumnIndex(COLUMN_NAME)),
                        cursor.getString(cursor.getColumnIndex(COLUMN_PATH))
                });
            }
        } finally {
            cursor.close();
        }
        return row;
    }

    public ArrayList<String[]> getSmbList() {
        SQLiteDatabase sqLiteDatabase = getReadableDatabase();

        Cursor cursor = sqLiteDatabase.query(getTableForOperation(Operation.SMB), null,
                null, null, null, null, null);
        cursor.moveToFirst();
        ArrayList<String[]> row = new ArrayList<>();
        try {

            while (cursor.moveToNext()) {
                try {
                    row.add(new String[] {
                            cursor.getString(cursor.getColumnIndex(COLUMN_NAME)),
                            SmbUtil.getSmbDecryptedPath(context, cursor.getString(cursor.getColumnIndex(COLUMN_PATH)))
                    });
                } catch (CryptException e) {
                    e.printStackTrace();

                    // failing to decrypt the path, removing entry from database
                    Toast.makeText(context,
                            context.getResources().getString(R.string.failed_smb_decrypt_path),
                            Toast.LENGTH_LONG).show();
                    removeSmbPath(cursor.getString(cursor.getColumnIndex(COLUMN_NAME)),
                            "");
                    continue;
                }
            }
        } finally {
            cursor.close();
        }
        return row;
    }

    public void removeHistoryPath(String path) {
        removePath(Operation.HISTORY, path);
    }

    public void removeHiddenPath(String path) {
        removePath(Operation.HIDDEN, path);
    }

    public void removeListViewPath(String path) {
        removePath(Operation.LIST, path);
    }

    public void removeGridViewPath(String path) {
        removePath(Operation.GRID, path);
    }

    public void removeBookmarksPath(String name, String path) {

        SQLiteDatabase sqLiteDatabase = getWritableDatabase();

        sqLiteDatabase.delete(TABLE_BOOKMARKS, COLUMN_NAME + " = ? AND " + COLUMN_PATH + " = ?",
                new String[] {name, path});
    }

    /**
     * Remove SMB entry
     * @param name
     * @param path the path we get from saved runtime variables is a decrypted, to remove entry,
     *             we must encrypt it's password fiend first first
     */
    public void removeSmbPath(String name, String path) {

        SQLiteDatabase sqLiteDatabase = getWritableDatabase();

        try {
            if (path.equals("")) {
                // we don't have a path, remove the entry with this name
                throw new CryptException();
            }

            sqLiteDatabase.delete(TABLE_SMB, COLUMN_NAME + " = ? AND " + COLUMN_PATH + " = ?",
                    new String[] {name, SmbUtil.getSmbEncryptedPath(context, path)});
        } catch (CryptException e) {
            e.printStackTrace();
            // force remove entry, we end up deleting all entries with same name

            sqLiteDatabase.delete(TABLE_SMB, COLUMN_NAME + " = ?",
                    new String[] {name});
        }
    }

    public void clearHistoryTable() {
        clearTable(Operation.HISTORY);
    }

    public void clearHiddenTable() {
        clearTable(Operation.HIDDEN);
    }

    public void clearListViewTable() {
        clearTable(Operation.LIST);
    }

    public void clearGridViewTable() {
        clearTable(Operation.GRID);
    }

    public void clearBookmarksTable() {
        clearTable(Operation.BOOKMARKS);
    }

    public void clearSmbTable() {
        clearTable(Operation.SMB);
    }

    public void renameBookmark(String oldName, String oldPath, String newName, String newPath) {
        renamePath(Operation.BOOKMARKS, oldName, oldPath, newName, newPath);
    }

    public void renameSMB(String oldName, String oldPath, String newName, String newPath) {
        renamePath(Operation.SMB, oldName, oldPath, newName, newPath);
    }

    private void setPath(Operation operation, String path) {
        SQLiteDatabase sqLiteDatabase = getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_PATH, path);

        sqLiteDatabase.insert(getTableForOperation(operation), null, contentValues);
    }

    private void setPath(Operation operation, String name, String path) {
        SQLiteDatabase sqLiteDatabase = getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_NAME, name);
        contentValues.put(COLUMN_PATH, path);

        sqLiteDatabase.insert(getTableForOperation(operation), null, contentValues);
    }

    private ArrayList<String> getPath(Operation operation) {

        SQLiteDatabase sqLiteDatabase = getReadableDatabase();
        Cursor cursor = sqLiteDatabase.query(getTableForOperation(operation), null,
                null, null, null, null, null);
        cursor.moveToFirst();

        switch (operation) {
            case HISTORY:
            case HIDDEN:
            case LIST:
            case GRID:
                ArrayList<String> paths = new ArrayList<>();
                try {

                    while (cursor.moveToNext()) {
                        paths.add(cursor.getString(cursor.getColumnIndex(COLUMN_PATH)));
                    }
                } finally {
                    cursor.close();
                }
                return paths;
            default:
                return null;
        }
    }

    private void removePath(Operation operation, String path) {

        SQLiteDatabase sqLiteDatabase = getWritableDatabase();

        sqLiteDatabase.delete(getTableForOperation(operation), COLUMN_PATH + "=?",
                new String[] {path});
    }

    private void clearTable(Operation operation) {

        SQLiteDatabase sqLiteDatabase = getWritableDatabase();

        sqLiteDatabase.delete(getTableForOperation(operation), COLUMN_PATH + "=?",
                new String[] { "NOT NULL" });
    }

    private void renamePath(Operation operation, String name, String path) {
        SQLiteDatabase sqLiteDatabase = getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_NAME, name);
        contentValues.put(COLUMN_PATH, path);

        sqLiteDatabase.update(getTableForOperation(operation), contentValues,
                COLUMN_PATH + "=?", new String[] {name});
    }

    private void renamePath(Operation operation, String oldName, String oldPath,
                               String newName, String newPath) {
        SQLiteDatabase sqLiteDatabase = getWritableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put(COLUMN_NAME, newName);
        contentValues.put(COLUMN_PATH, newPath);

        sqLiteDatabase.update(getTableForOperation(operation), contentValues, COLUMN_NAME
                + "=? AND " + COLUMN_PATH + "=?", new String[] {oldName, oldPath});
        return;
    }

    /**
     * Return table string for corresponding {@link Operation}
     * @param operation
     * @return
     */
    private String getTableForOperation(Operation operation) {

        switch (operation) {
            case HISTORY:
                return TABLE_HISTORY;
            case HIDDEN:
                return TABLE_HIDDEN;
            case LIST:
                return TABLE_LIST;
            case GRID:
                return TABLE_GRID;
            case BOOKMARKS:
                return TABLE_BOOKMARKS;
            case SMB:
                return TABLE_SMB;
            default:
                return null;
        }
    }
}