/*******************************************************************************
 *      Copyright (C) 2012 Google Inc.
 *      Licensed to 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.android.mail.providers;

import android.database.Cursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.ContactsContract;
import android.app.SearchManager;
import android.content.ContentResolver;
import android.content.Context;
import android.text.TextUtils;

import com.android.mail.R;
import com.android.mail.utils.MatrixCursorWithCachedColumns;

import java.util.ArrayList;

/**
 * Simple extension / instantiation of SearchRecentSuggestionsProvider, independent
 * of mail account or account capabilities.  Offers suggestions from historical searches
 * and contact email addresses on the device.
 */
public class SuggestionsProvider extends SearchRecentSuggestionsProvider {
    /**
     * Columns over the contacts database that we return in the {@link ContactsCursor}.
     */
    private static final String[] CONTACTS_COLUMNS = new String[] {
            BaseColumns._ID,
            SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_QUERY,
            SearchManager.SUGGEST_COLUMN_ICON_1
    };
    private ArrayList<String> mFullQueryTerms;
    /** Used for synchronization */
    private final Object mTermsLock = new Object();
    private final static String[] sContract = new String[] {
            ContactsContract.CommonDataKinds.Email.DISPLAY_NAME,
            ContactsContract.CommonDataKinds.Email.DATA
    };
    /**
     * Minimum length of query before we start showing contacts suggestions.
     */
    static private final int MIN_QUERY_LENGTH_FOR_CONTACTS = 2;

    public SuggestionsProvider(Context context) {
        super(context);
    }

    @Override
    public Cursor query(String query) {
        Cursor mergeCursor = null;

        synchronized (mTermsLock) {
            mFullQueryTerms = null;
            super.setFullQueryTerms(mFullQueryTerms);
        }
        // Get the custom suggestions for email which are from, to, etc.
        if (query != null) {
            // Tokenize the query.
            String[] tokens = TextUtils.split(query,
                    SearchRecentSuggestionsProvider.QUERY_TOKEN_SEPARATOR);
            // There are multiple tokens, so query on the last token only.
            if (tokens != null && tokens.length > 1) {
                query = tokens[tokens.length - 1];
                // Leave off the last token since we are auto completing on it.
                synchronized (mTermsLock) {
                    mFullQueryTerms = new ArrayList<String>();
                    for (int i = 0, size = tokens.length - 1; i < size; i++) {
                        mFullQueryTerms.add(tokens[i]);
                    }
                    super.setFullQueryTerms(mFullQueryTerms);
                }
            } else {
                // Strip excess whitespace.
                query = query.trim();
            }
            ArrayList<Cursor> cursors = new ArrayList<Cursor>();
            // Pass query; at this point it is either the last term OR the
            // only term.
            final Cursor c = super.query(query);
            if (c != null) {
                cursors.add(c);
            }

            if (query.length() >= MIN_QUERY_LENGTH_FOR_CONTACTS) {
                cursors.add(new ContactsCursor().query(query));
            }

            if (cursors.size() > 0) {
                mergeCursor = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
            }
        }
        return mergeCursor;
    }

    /**
     * Utility class to return a cursor over the contacts database
     */
    private final class ContactsCursor extends MatrixCursorWithCachedColumns {
        public ContactsCursor() {
            super(CONTACTS_COLUMNS);
        }

        /**
         * Searches over the contacts cursor with the specified query as the starting characters to
         * match.
         * @param query
         * @return a cursor over the contacts database with the contacts matching the query.
         */
        public ContactsCursor query(String query) {
            final Uri contactsUri = Uri.withAppendedPath(
                    ContactsContract.CommonDataKinds.Email.CONTENT_FILTER_URI, Uri.encode(query));
            final Cursor cursor = mContext.getContentResolver().query(
                    contactsUri, sContract, null, null, null);
            // We don't want to show a contact icon here. Leaving the SEARCH_ICON_1 field
            // empty causes inconsistent behavior because the cursor is merged with the
            // historical suggestions, which have an icon.  The solution is to show an empty icon
            // instead.
            final String emptyIcon = ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
                    + mContext.getPackageName() + "/" + R.drawable.empty;
            if (cursor != null) {
                final int nameIndex = cursor
                        .getColumnIndex(ContactsContract.CommonDataKinds.Email.DISPLAY_NAME);
                final int addressIndex = cursor
                        .getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA);
                String match;
                while (cursor.moveToNext()) {
                    match = cursor.getString(nameIndex);
                    match = !TextUtils.isEmpty(match) ? match : cursor.getString(addressIndex);
                    // The order of fields is:
                    // _ID, SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_QUERY, SUGGEST_COLUMN_ICON_1
                    addRow(new Object[] {0, match, createQuery(match), emptyIcon});
                }
                cursor.close();
            }
            return this;
        }
    }

    private String createQuery(String inMatch) {
        final StringBuilder query = new StringBuilder();
        if (mFullQueryTerms != null) {
            synchronized (mTermsLock) {
                for (String token : mFullQueryTerms) {
                    query.append(token).append(QUERY_TOKEN_SEPARATOR);
                }
            }
        }
        // Append the match as well.
        query.append(inMatch);
        // Example:
        // Search terms in the searchbox are : "pdf test*"
        // Contacts database contains: test@tester.com, test@other.com
        // If the user taps "test@tester.com", the query passed with
        // ACTION_SEARCH is:
        // "pdf test@tester.com"
        // If the user taps "test@other.com", the query passed with
        // ACTION_SEARCH is:
        // "pdf test@other.com"
        return query.toString();
    }
}