/*
 * Copyright (c) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.liskovsoft.smartyoutubetv2.tv.old.data;

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import androidx.annotation.NonNull;

import java.util.HashMap;

/**
 * VideoProvider is a ContentProvider that provides videos for the rest of applications.
 */
public class VideoProvider extends ContentProvider {
    private static final UriMatcher sUriMatcher = buildUriMatcher();
    private VideoDbHelper mOpenHelper;

    // These codes are returned from sUriMatcher#match when the respective Uri matches.
    private static final int VIDEO = 1;
    private static final int VIDEO_WITH_CATEGORY = 2;
    private static final int SEARCH_SUGGEST = 3;
    private static final int REFRESH_SHORTCUT = 4;

    private static final SQLiteQueryBuilder sVideosContainingQueryBuilder;
    private static final String[] sVideosContainingQueryColumns;
    private static final HashMap<String, String> sColumnMap = buildColumnMap();
    private ContentResolver mContentResolver;

    @Override
    public boolean onCreate() {
        Context context = getContext();
        mContentResolver = context.getContentResolver();
        mOpenHelper = new VideoDbHelper(context);
        return true;
    }

    static {
        sVideosContainingQueryBuilder = new SQLiteQueryBuilder();
        sVideosContainingQueryBuilder.setTables(VideoContract.VideoEntry.TABLE_NAME);
        sVideosContainingQueryBuilder.setProjectionMap(sColumnMap);
        sVideosContainingQueryColumns = new String[]{
                VideoContract.VideoEntry._ID,
                VideoContract.VideoEntry.COLUMN_NAME,
                VideoContract.VideoEntry.COLUMN_CATEGORY,
                VideoContract.VideoEntry.COLUMN_DESC,
                VideoContract.VideoEntry.COLUMN_VIDEO_URL,
                VideoContract.VideoEntry.COLUMN_BG_IMAGE_URL,
                VideoContract.VideoEntry.COLUMN_STUDIO,
                VideoContract.VideoEntry.COLUMN_CARD_IMG,
                VideoContract.VideoEntry.COLUMN_CONTENT_TYPE,
                VideoContract.VideoEntry.COLUMN_IS_LIVE,
                VideoContract.VideoEntry.COLUMN_VIDEO_WIDTH,
                VideoContract.VideoEntry.COLUMN_VIDEO_HEIGHT,
                VideoContract.VideoEntry.COLUMN_AUDIO_CHANNEL_CONFIG,
                VideoContract.VideoEntry.COLUMN_PURCHASE_PRICE,
                VideoContract.VideoEntry.COLUMN_RENTAL_PRICE,
                VideoContract.VideoEntry.COLUMN_RATING_STYLE,
                VideoContract.VideoEntry.COLUMN_RATING_SCORE,
                VideoContract.VideoEntry.COLUMN_PRODUCTION_YEAR,
                VideoContract.VideoEntry.COLUMN_DURATION,
                VideoContract.VideoEntry.COLUMN_ACTION,
                SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID
        };
    }

    static UriMatcher buildUriMatcher() {
        final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
        final String authority = VideoContract.CONTENT_AUTHORITY;

        // For each type of URI to add, create a corresponding code.
        matcher.addURI(authority, VideoContract.PATH_VIDEO, VIDEO);
        matcher.addURI(authority, VideoContract.PATH_VIDEO + "/*", VIDEO_WITH_CATEGORY);

        // Search related URIs.
        matcher.addURI(authority, "search/" + SearchManager.SUGGEST_URI_PATH_QUERY, SEARCH_SUGGEST);
        matcher.addURI(authority, "search/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*", SEARCH_SUGGEST);
        return matcher;
    }

    private Cursor getSuggestions(String query) {
        query = query.toLowerCase();
        return sVideosContainingQueryBuilder.query(
                mOpenHelper.getReadableDatabase(),
                sVideosContainingQueryColumns,
                VideoContract.VideoEntry.COLUMN_NAME + " LIKE ? OR " +
                        VideoContract.VideoEntry.COLUMN_DESC + " LIKE ?",
                new String[]{"%" + query + "%", "%" + query + "%"},
                null,
                null,
                null
        );
    }

    private static HashMap<String, String> buildColumnMap() {
        HashMap<String, String> map = new HashMap<>();
        map.put(VideoContract.VideoEntry._ID, VideoContract.VideoEntry._ID);
        map.put(VideoContract.VideoEntry.COLUMN_NAME, VideoContract.VideoEntry.COLUMN_NAME);
        map.put(VideoContract.VideoEntry.COLUMN_DESC, VideoContract.VideoEntry.COLUMN_DESC);
        map.put(VideoContract.VideoEntry.COLUMN_CATEGORY, VideoContract.VideoEntry.COLUMN_CATEGORY);
        map.put(VideoContract.VideoEntry.COLUMN_VIDEO_URL,
                VideoContract.VideoEntry.COLUMN_VIDEO_URL);
        map.put(VideoContract.VideoEntry.COLUMN_BG_IMAGE_URL,
                VideoContract.VideoEntry.COLUMN_BG_IMAGE_URL);
        map.put(VideoContract.VideoEntry.COLUMN_CARD_IMG, VideoContract.VideoEntry.COLUMN_CARD_IMG);
        map.put(VideoContract.VideoEntry.COLUMN_STUDIO, VideoContract.VideoEntry.COLUMN_STUDIO);
        map.put(VideoContract.VideoEntry.COLUMN_CONTENT_TYPE,
                VideoContract.VideoEntry.COLUMN_CONTENT_TYPE);
        map.put(VideoContract.VideoEntry.COLUMN_IS_LIVE, VideoContract.VideoEntry.COLUMN_IS_LIVE);
        map.put(VideoContract.VideoEntry.COLUMN_VIDEO_WIDTH,
                VideoContract.VideoEntry.COLUMN_VIDEO_WIDTH);
        map.put(VideoContract.VideoEntry.COLUMN_VIDEO_HEIGHT,
                VideoContract.VideoEntry.COLUMN_VIDEO_HEIGHT);
        map.put(VideoContract.VideoEntry.COLUMN_AUDIO_CHANNEL_CONFIG,
                VideoContract.VideoEntry.COLUMN_AUDIO_CHANNEL_CONFIG);
        map.put(VideoContract.VideoEntry.COLUMN_PURCHASE_PRICE,
                VideoContract.VideoEntry.COLUMN_PURCHASE_PRICE);
        map.put(VideoContract.VideoEntry.COLUMN_RENTAL_PRICE,
                VideoContract.VideoEntry.COLUMN_RENTAL_PRICE);
        map.put(VideoContract.VideoEntry.COLUMN_RATING_STYLE,
                VideoContract.VideoEntry.COLUMN_RATING_STYLE);
        map.put(VideoContract.VideoEntry.COLUMN_RATING_SCORE,
                VideoContract.VideoEntry.COLUMN_RATING_SCORE);
        map.put(VideoContract.VideoEntry.COLUMN_PRODUCTION_YEAR,
                VideoContract.VideoEntry.COLUMN_PRODUCTION_YEAR);
        map.put(VideoContract.VideoEntry.COLUMN_DURATION, VideoContract.VideoEntry.COLUMN_DURATION);
        map.put(VideoContract.VideoEntry.COLUMN_ACTION, VideoContract.VideoEntry.COLUMN_ACTION);
        map.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID, VideoContract.VideoEntry._ID + " AS " +
                SearchManager.SUGGEST_COLUMN_INTENT_DATA_ID);
        map.put(SearchManager.SUGGEST_COLUMN_SHORTCUT_ID,
                VideoContract.VideoEntry._ID + " AS " + SearchManager.SUGGEST_COLUMN_SHORTCUT_ID);
        return map;
    }

    @Override
    public Cursor query(@NonNull Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        Cursor retCursor;
        switch (sUriMatcher.match(uri)) {
            case SEARCH_SUGGEST: {
                String rawQuery = "";
                if (selectionArgs != null && selectionArgs.length > 0) {
                    rawQuery = selectionArgs[0];
                }
                retCursor = getSuggestions(rawQuery);
                break;
            }
            case VIDEO: {
                retCursor = mOpenHelper.getReadableDatabase().query(
                        VideoContract.VideoEntry.TABLE_NAME,
                        projection,
                        selection,
                        selectionArgs,
                        null,
                        null,
                        sortOrder
                );
                break;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        retCursor.setNotificationUri(mContentResolver, uri);
        return retCursor;
    }

    @Override
    public String getType(@NonNull Uri uri) {
        switch (sUriMatcher.match(uri)) {
            // The application is querying the db for its own contents.
            case VIDEO_WITH_CATEGORY:
                return VideoContract.VideoEntry.CONTENT_TYPE;
            case VIDEO:
                return VideoContract.VideoEntry.CONTENT_TYPE;

            // The Android TV global search is querying our app for relevant content.
            case SEARCH_SUGGEST:
                return SearchManager.SUGGEST_MIME_TYPE;
            case REFRESH_SHORTCUT:
                return SearchManager.SHORTCUT_MIME_TYPE;

            // We aren't sure what is being asked of us.
            default:
                throw new UnsupportedOperationException("Unknown uri: " + uri);
        }
    }

    @Override
    public Uri insert(@NonNull Uri uri, ContentValues values) {
        final Uri returnUri;
        final int match = sUriMatcher.match(uri);

        switch (match) {
            case VIDEO: {
                long _id = mOpenHelper.getWritableDatabase().insert(
                        VideoContract.VideoEntry.TABLE_NAME, null, values);
                if (_id > 0) {
                    returnUri = VideoContract.VideoEntry.buildVideoUri(_id);
                } else {
                    throw new SQLException("Failed to insert row into " + uri);
                }
                break;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        mContentResolver.notifyChange(uri, null);
        return returnUri;
    }

    @Override
    public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
        final int rowsDeleted;

        if (selection == null) {
            throw new UnsupportedOperationException("Cannot delete without selection specified.");
        }

        switch (sUriMatcher.match(uri)) {
            case VIDEO: {
                rowsDeleted = mOpenHelper.getWritableDatabase().delete(
                        VideoContract.VideoEntry.TABLE_NAME, selection, selectionArgs);
                break;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        if (rowsDeleted != 0) {
            mContentResolver.notifyChange(uri, null);
        }

        return rowsDeleted;
    }

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

        switch (sUriMatcher.match(uri)) {
            case VIDEO: {
                rowsUpdated = mOpenHelper.getWritableDatabase().update(
                        VideoContract.VideoEntry.TABLE_NAME, values, selection, selectionArgs);
                break;
            }
            default: {
                throw new UnsupportedOperationException("Unknown uri: " + uri);
            }
        }

        if (rowsUpdated != 0) {
            mContentResolver.notifyChange(uri, null);
        }

        return rowsUpdated;
    }

    @Override
    public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
        switch (sUriMatcher.match(uri)) {
            case VIDEO: {
                final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
                int returnCount = 0;

                db.beginTransaction();
                try {
                    for (ContentValues value : values) {
                        long _id = db.insertWithOnConflict(VideoContract.VideoEntry.TABLE_NAME,
                                null, value, SQLiteDatabase.CONFLICT_REPLACE);
                        if (_id != -1) {
                            returnCount++;
                        }
                    }
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }

                mContentResolver.notifyChange(uri, null);
                return returnCount;
            }
            default: {
                return super.bulkInsert(uri, values);
            }
        }
    }
}
