package com.datonicgroup.narrate.app.dataprovider.providers;

import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

import com.datonicgroup.narrate.app.dataprovider.providers.Contract.Entries;
import com.datonicgroup.narrate.app.dataprovider.providers.DatabaseHelper.Tables;
import com.datonicgroup.narrate.app.util.LogUtil;
import com.datonicgroup.narrate.app.dataprovider.SelectionBuilder;

import java.util.ArrayList;

/**
 * Created by timothymiko on 11/25/14.
 */
public class DataProvider extends ContentProvider {

    private static final UriMatcher sUriMatcher = buildUriMatcher();

    private static final int ENTRIES = 100;
    private static final int ENTRIES_ID = 101;

    private DatabaseHelper mDatabaseHelper;

    private static UriMatcher buildUriMatcher() {
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = Contract.AUTHORITY;

        // Add a pattern that routes URIs terminated with "entries" to an ENTRIES operation
        // that is performed on the whole table
        matcher.addURI(authority, "entries", ENTRIES);

        // Add a pattern that routes URIs terminated with a number to an ENTRIES operation
        // that is aimed at a specific entry where the path ends with its uuid
        matcher.addURI(authority, "entries/*", ENTRIES_ID);

        return matcher;
    }

    @Override
    public boolean onCreate() {
        mDatabaseHelper = DatabaseHelper.getInstance(getContext());
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
            case ENTRIES:
            case ENTRIES_ID:
                return Entries.CONTENT_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
        final SelectionBuilder builder = new SelectionBuilder();

        final int match = sUriMatcher.match(uri);
        switch (match) {
            case ENTRIES: {
                builder
                        .table(Tables.ENTRIES)
                        .map(Entries.PROJ_ALL);
                break;
            }
            case ENTRIES_ID: {
                builder
                        .table(Tables.ENTRIES)
                        .where(Entries.UUID + "=?", uri.getLastPathSegment())
                        .map(Entries.PROJ_ALL);
                break;
            }
            default: {
                throw new IllegalArgumentException("Unknown URI: " + uri);
            }
        }


        Cursor c =
                builder
                        .where(selection, selectionArgs)
                        .query(db, false, projection, sortOrder, null);

        Context context = getContext();
        if (null != context) {
            c.setNotificationUri(context.getContentResolver(), uri);
        }
        return c;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {

        final int match = sUriMatcher.match(uri);
        SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();

        switch (match) {
            case ENTRIES: {
                db.insertOrThrow(Tables.ENTRIES, null, values);
                notifyChange(uri);
                return Entries.buildEntryUri(values.getAsString(Entries.UUID));
            }
            default: {
                throw new UnsupportedOperationException("Unknown insert uri: " + uri);
            }
        }
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        LogUtil.log(DataProvider.class.getSimpleName(), "delete(uri=" + uri + ")");

        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
        final SelectionBuilder builder = new SelectionBuilder();

        final int match = sUriMatcher.match(uri);
        switch (match) {
            case ENTRIES: {
                builder.table(Tables.ENTRIES);
                break;
            }
            case ENTRIES_ID: {
                builder
                        .table(Tables.ENTRIES)
                        .where(Entries.UUID + "=?", uri.getLastPathSegment());
                break;
            }
            default: {
                throw new IllegalArgumentException("Unknown URI: " + uri);
            }
        }

        int count = builder.where(selection, selectionArgs).delete(db);
        notifyChange(uri);
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
        final SelectionBuilder builder = new SelectionBuilder();

        final int match = sUriMatcher.match(uri);
        switch (match) {
            case ENTRIES: {
                builder.table(Tables.ENTRIES);
                break;
            }
            case ENTRIES_ID: {
                builder
                        .table(Tables.ENTRIES)
                        .where(Entries.UUID + "=?", uri.getLastPathSegment());
                break;
            }
            default: {
                throw new IllegalArgumentException("Unknown URI: " + uri);
            }
        }

        int count = builder.where(selection, selectionArgs).update(db, values);
        notifyChange(uri);
        return count;
    }

    /**
     * Apply the given set of {@link ContentProviderOperation}, executing inside
     * a {@link SQLiteDatabase} transaction. All changes will be rolled back if
     * any single one fails.
     */
    @Override
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
        final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
        db.beginTransaction();
        try {
            final int numOperations = operations.size();
            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
            for (int i = 0; i < numOperations; i++) {
                results[i] = operations.get(i).apply(this, results, i);
            }
            db.setTransactionSuccessful();
            return results;
        } finally {
            db.endTransaction();
        }
    }

    private void notifyChange(Uri uri) {
        // We only notify changes if the caller is not the sync adapter.
        // The sync adapter has the responsibility of notifying changes (it can do so
        // more intelligently than we can -- for example, doing it only once at the end
        // of the sync instead of issuing thousands of notifications for each record).
        if (!Contract.hasCallerIsSyncAdapterParameter(uri)) {
            Context context = getContext();
            context.getContentResolver().notifyChange(uri, null);

            // Widgets can't register content observers so we refresh widgets separately.
//            context.sendBroadcast(ScheduleWidgetProvider.getRefreshBroadcastIntent(context, false));
            // TODO: add a broadcast to notify of updates
        }
    }
}