package org.fdroid.fdroid.data;

import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import org.fdroid.fdroid.data.Schema.AppPrefsTable;
import org.fdroid.fdroid.data.Schema.AppPrefsTable.Cols;

public class AppPrefsProvider extends FDroidProvider {

    public static final class Helper {
        private Helper() { }

        public static void update(Context context, App app, AppPrefs prefs) {
            ContentValues values = new ContentValues(3);
            values.put(Cols.IGNORE_ALL_UPDATES, prefs.ignoreAllUpdates);
            values.put(Cols.IGNORE_THIS_UPDATE, prefs.ignoreThisUpdate);

            if (getPrefsOrNull(context, app) == null) {
                values.put(Cols.PACKAGE_NAME, app.packageName);
                context.getContentResolver().insert(getContentUri(), values);
            } else {
                context.getContentResolver().update(getAppUri(app.packageName), values, null, null);
            }
        }

        @NonNull
        public static AppPrefs getPrefsOrDefault(Context context, App app) {
            AppPrefs prefs = getPrefsOrNull(context, app);
            return prefs == null ? AppPrefs.createDefault() : prefs;
        }

        @Nullable
        public static AppPrefs getPrefsOrNull(Context context, App app) {
            Cursor cursor = context.getContentResolver().query(getAppUri(app.packageName), Cols.ALL, null, null, null);
            if (cursor == null) {
                return null;
            }

            try {
                if (cursor.getCount() == 0) {
                    return null;
                }

                cursor.moveToFirst();
                return new AppPrefs(
                        cursor.getInt(cursor.getColumnIndexOrThrow(Cols.IGNORE_THIS_UPDATE)),
                        cursor.getInt(cursor.getColumnIndexOrThrow(Cols.IGNORE_ALL_UPDATES)) > 0);
            } finally {
                cursor.close();
            }
        }
    }

    private class Query extends QueryBuilder {

        @Override
        protected String getRequiredTables() {
            return AppPrefsTable.NAME;
        }

        @Override
        public void addField(String field) {
            appendField(field, getTableName());
        }
    }

    private static final String PROVIDER_NAME = "AppPrefsProvider";

    private static final UriMatcher MATCHER = new UriMatcher(-1);

    private static final String PATH_PACKAGE_NAME = "packageName";

    static {
        MATCHER.addURI(getAuthority(), PATH_PACKAGE_NAME + "/*", CODE_SINGLE);
    }

    private static Uri getContentUri() {
        return Uri.parse("content://" + getAuthority());
    }

    public static Uri getAppUri(String packageName) {
        return getContentUri().buildUpon().appendPath(PATH_PACKAGE_NAME).appendPath(packageName).build();
    }

    @Override
    protected String getTableName() {
        return AppPrefsTable.NAME;
    }

    @Override
    protected String getProviderName() {
        return "AppPrefsProvider";
    }

    public static String getAuthority() {
        return AUTHORITY + "." + PROVIDER_NAME;
    }

    @Override
    protected UriMatcher getMatcher() {
        return MATCHER;
    }

    protected QuerySelection querySingle(String packageName) {
        final String selection = getTableName() + "." + Cols.PACKAGE_NAME + " = ?";
        final String[] args = {packageName};
        return new QuerySelection(selection, args);
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String customSelection, String[] selectionArgs, String sortOrder) {
        if (MATCHER.match(uri) != CODE_SINGLE) {
            throw new UnsupportedOperationException("Invalid URI for app content provider: " + uri);
        }

        QuerySelection selection = new QuerySelection(customSelection, selectionArgs)
                .add(querySingle(uri.getLastPathSegment()));

        Query query = new Query();
        query.addSelection(selection);
        query.addFields(projection);
        query.addOrderBy(sortOrder);

        Cursor cursor = LoggingQuery.query(db(), query.toString(), query.getArgs());
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }

    @Override
    public int delete(Uri uri, String where, String[] whereArgs) {
        throw new UnsupportedOperationException("Delete not supported for " + uri + ".");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        db().insertOrThrow(getTableName(), null, values);
        getContext().getContentResolver().notifyChange(AppProvider.getCanUpdateUri(), null);
        return getAppUri(values.getAsString(Cols.PACKAGE_NAME));
    }

    @Override
    public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
        if (MATCHER.match(uri) != CODE_SINGLE) {
            throw new UnsupportedOperationException("Update not supported for " + uri + ".");
        }

        QuerySelection query = new QuerySelection(where, whereArgs).add(querySingle(uri.getLastPathSegment()));
        int count = db().update(getTableName(), values, query.getSelection(), query.getArgs());
        getContext().getContentResolver().notifyChange(AppProvider.getCanUpdateUri(), null);
        return count;
    }
}
