package com.fsck.k9.ui;


import android.content.ActivityNotFoundException;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Intents;
import android.provider.ContactsContract.QuickContact;
import android.provider.ContactsContract.RawContacts;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;

import com.fsck.k9.mail.Address;

import de.hdodenhof.circleimageview.CircleImageView;

/**
 * ContactBadge replaces the android ContactBadge for custom drawing.
 * <p>
 * Based on QuickContactBadge:
 * https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/widget/QuickContactBadge.java
 */
public class ContactBadge extends CircleImageView implements OnClickListener {
    private static final int TOKEN_EMAIL_LOOKUP = 0;
    private static final int TOKEN_EMAIL_LOOKUP_AND_TRIGGER = 1;

    private static final String EXTRA_URI_CONTENT = "uri_content";

    private static final String[] EMAIL_LOOKUP_PROJECTION = new String[] {
            RawContacts.CONTACT_ID,
            Contacts.LOOKUP_KEY,
    };
    private static final int EMAIL_ID_COLUMN_INDEX = 0;
    private static final int EMAIL_LOOKUP_STRING_COLUMN_INDEX = 1;


    private Uri contactUri;
    private String contactEmail;
    private QueryHandler queryHandler;
    private Bundle extras = null;


    public ContactBadge(Context context) {
        this(context, null);
    }

    public ContactBadge(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ContactBadge(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        queryHandler = new QueryHandler(context.getContentResolver());
        setOnClickListener(this);
    }

    /**
     * True if a contact, an email address or a phone number has been assigned
     */
    private boolean isAssigned() {
        return contactUri != null || contactEmail != null;
    }

    /**
     * Assign the contact uri that this ContactBadge should be associated
     * with. Note that this is only used for displaying the QuickContact window and
     * won't bind the contact's photo for you. Call {@link #setImageDrawable(Drawable)} to set the
     * photo.
     *
     * @param contactUri
     *         Either a {@link Contacts#CONTENT_URI} or
     *         {@link Contacts#CONTENT_LOOKUP_URI} style URI.
     */
    public void assignContactUri(Uri contactUri) {
        this.contactUri = contactUri;
        contactEmail = null;
        onContactUriChanged();
    }

    /**
     * Assign a contact based on an email address. This should only be used when
     * the contact's URI is not available, as an extra query will have to be
     * performed to lookup the URI based on the email.
     *
     * @param emailAddress
     *         The email address of the contact.
     * @param lazyLookup
     *         If this is true, the lookup query will not be performed
     *         until this view is clicked.
     * @param extras
     *         A bundle of extras to populate the contact edit page with if the contact
     *         is not found and the user chooses to add the email address to an existing contact or
     *         create a new contact. Uses the same string constants as those found in
     *         {@link android.provider.ContactsContract.Intents.Insert}
     */

    public void assignContactFromEmail(String emailAddress, boolean lazyLookup, Bundle extras) {
        contactEmail = emailAddress;
        this.extras = extras;
        if (!lazyLookup) {
            queryHandler.startQuery(TOKEN_EMAIL_LOOKUP, null,
                    Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(contactEmail)),
                    EMAIL_LOOKUP_PROJECTION, null, null, null);
        } else {
            contactUri = null;
            onContactUriChanged();
        }
    }

    private void onContactUriChanged() {
        setEnabled(isAssigned());
    }

    @Override
    public void onClick(View v) {
        // If contact has been assigned, extras should no longer be null, but do a null check
        // anyway just in case assignContactFromPhone or Email was called with a null bundle or
        // wasn't assigned previously.
        final Bundle extras = (this.extras == null) ? new Bundle() : this.extras;
        if (contactUri != null) {
            QuickContact.showQuickContact(getContext(), ContactBadge.this, contactUri,
                    QuickContact.MODE_LARGE, null);
        } else if (contactEmail != null) {
            extras.putString(EXTRA_URI_CONTENT, contactEmail);
            queryHandler.startQuery(TOKEN_EMAIL_LOOKUP_AND_TRIGGER, extras,
                    Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, Uri.encode(contactEmail)),
                    EMAIL_LOOKUP_PROJECTION, null, null, null);
        }
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(ContactBadge.class.getName());
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(ContactBadge.class.getName());
    }

    /**
     * Assign the contact to the badge.
     *
     * On 4.3, we pass the address name as extra info so that if the contact doesn't exist
     * the name is auto-populated.
     *
     * @param address the address to look for a contact for.
     */
    public void setContact(Address address) {
        Bundle extraContactInfo = new Bundle();
        extraContactInfo.putString(ContactsContract.Intents.Insert.NAME, address.getPersonal());
        assignContactFromEmail(address.getAddress(), true, extraContactInfo);
    }


    private class QueryHandler extends AsyncQueryHandler {

        QueryHandler(ContentResolver cr) {
            super(cr);
        }

        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            Uri lookupUri = null;
            Uri createUri = null;
            boolean trigger = false;
            Bundle extras = (cookie != null) ? (Bundle) cookie : new Bundle();
            try {
                switch (token) {
                    case TOKEN_EMAIL_LOOKUP_AND_TRIGGER:
                        trigger = true;
                        createUri = Uri.fromParts("mailto",
                                extras.getString(EXTRA_URI_CONTENT), null);

                        //$FALL-THROUGH$
                    case TOKEN_EMAIL_LOOKUP: {
                        if (cursor != null && cursor.moveToFirst()) {
                            long contactId = cursor.getLong(EMAIL_ID_COLUMN_INDEX);
                            String lookupKey = cursor.getString(EMAIL_LOOKUP_STRING_COLUMN_INDEX);
                            lookupUri = Contacts.getLookupUri(contactId, lookupKey);
                        }
                        break;
                    }
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }

            contactUri = lookupUri;
            onContactUriChanged();

            if (trigger && lookupUri != null) {
                // Found contact, so trigger QuickContact
                QuickContact.showQuickContact(
                        getContext(), ContactBadge.this, lookupUri, QuickContact.MODE_LARGE, null);
            } else if (createUri != null) {
                // Prompt user to add this person to contacts
                try {
                    final Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, createUri);
                    extras.remove(EXTRA_URI_CONTENT);
                    intent.putExtras(extras);
                    getContext().startActivity(intent);
                } catch (ActivityNotFoundException e) {
                    Toast.makeText(getContext(), R.string.error_activity_not_found, Toast.LENGTH_LONG).show();
                }
            }
        }
    }
}

