/*
 * Copyright (C) 2015 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.contacts.editor;

import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
import com.android.contacts.activities.CompactContactEditorActivity;
import com.android.contacts.common.model.RawContactDelta;
import com.android.contacts.common.model.ValuesDelta;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.ContactPhotoUtils;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.io.FileNotFoundException;
import java.util.ArrayList;

/**
 * Contact editor with only the most important fields displayed initially.
 */
public class CompactContactEditorFragment extends ContactEditorBaseFragment implements
        CompactRawContactsEditorView.Listener, CompactPhotoEditorView.Listener {

    private static final String KEY_PHOTO_RAW_CONTACT_ID = "photo_raw_contact_id";
    private static final String KEY_UPDATED_PHOTOS = "updated_photos";

    private long mPhotoRawContactId;
    private Bundle mUpdatedPhotos = new Bundle();

    @Override
    public void onCreate(Bundle savedState) {
        super.onCreate(savedState);

        if (savedState != null) {
            mPhotoRawContactId = savedState.getLong(KEY_PHOTO_RAW_CONTACT_ID);
            mUpdatedPhotos = savedState.getParcelable(KEY_UPDATED_PHOTOS);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
        setHasOptionsMenu(true);

        final View view = inflater.inflate(
                R.layout.compact_contact_editor_fragment, container, false);
        mContent = (LinearLayout) view.findViewById(R.id.raw_contacts_editor_view);
        return view;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putLong(KEY_PHOTO_RAW_CONTACT_ID, mPhotoRawContactId);
        outState.putParcelable(KEY_UPDATED_PHOTOS, mUpdatedPhotos);
        super.onSaveInstanceState(outState);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == android.R.id.home) {
            return revert();
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void bindEditors() {
        if (!isReadyToBindEditors()) {
            return;
        }

        // Add input fields for the loaded Contact
        final CompactRawContactsEditorView editorView = getContent();
        editorView.setListener(this);
        editorView.setState(mState, getMaterialPalette(), mViewIdGenerator, mPhotoId,
                mHasNewContact, mIsUserProfile, mAccountWithDataSet);
        if (mHasNewContact && !TextUtils.isEmpty(mReadOnlyDisplayName)) {
            mReadOnlyNameEditorView = editorView.getPrimaryNameEditorView();
            editorView.maybeSetReadOnlyDisplayNameAsPrimary(mReadOnlyDisplayName);
        }

        // Set up the photo widget
        editorView.setPhotoListener(this);
        mPhotoRawContactId = editorView.getPhotoRawContactId();
        // If there is an updated full resolution photo apply it now, this will be the case if
        // the user selects or takes a new photo, then rotates the device.
        final Uri uri = (Uri) mUpdatedPhotos.get(String.valueOf(mPhotoRawContactId));
        if (uri != null) {
            editorView.setFullSizePhoto(uri);
        }

        // The editor is ready now so make it visible
        editorView.setEnabled(isEnabled());
        editorView.setVisibility(View.VISIBLE);

        // Refresh the ActionBar as the visibility of the join command
        // Activity can be null if we have been detached from the Activity.
        invalidateOptionsMenu();
    }

    private boolean isReadyToBindEditors() {
        if (mState.isEmpty()) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "No data to bind editors");
            }
            return false;
        }
        if (mIsEdit && !mExistingContactDataReady) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Existing contact data is not ready to bind editors.");
            }
            return false;
        }
        if (mHasNewContact && !mNewContactDataReady) {
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "New contact data is not ready to bind editors.");
            }
            return false;
        }
        return true;
    }

    @Override
    protected View getAggregationAnchorView(long rawContactId) {
        return getContent().getAggregationAnchorView();
    }

    @Override
    protected void setGroupMetaData() {
        if (mGroupMetaData != null) {
            getContent().setGroupMetaData(mGroupMetaData);
        }
    }

    @Override
    protected boolean doSaveAction(int saveMode, Long joinContactId) {
        final Intent intent = ContactSaveService.createSaveContactIntent(mContext, mState,
                SAVE_MODE_EXTRA_KEY, saveMode, isEditingUserProfile(),
                ((Activity) mContext).getClass(),
                CompactContactEditorActivity.ACTION_SAVE_COMPLETED, mUpdatedPhotos,
                JOIN_CONTACT_ID_EXTRA_KEY, joinContactId);
        return startSaveService(mContext, intent, saveMode);
    }

    @Override
    protected void joinAggregate(final long contactId) {
        final Intent intent = ContactSaveService.createJoinContactsIntent(
                mContext, mContactIdForJoin, contactId, CompactContactEditorActivity.class,
                CompactContactEditorActivity.ACTION_JOIN_COMPLETED);
        mContext.startService(intent);
    }

    public void removePhoto() {
        getContent().removePhoto();
        mUpdatedPhotos.remove(String.valueOf(mPhotoRawContactId));
    }

    public void updatePhoto(Uri uri) throws FileNotFoundException {
        final Bitmap bitmap = ContactPhotoUtils.getBitmapFromUri(getActivity(), uri);
        if (bitmap == null || bitmap.getHeight() <= 0 || bitmap.getWidth() <= 0) {
            Toast.makeText(mContext, R.string.contactPhotoSavedErrorToast,
                    Toast.LENGTH_SHORT).show();
            return;
        }
        mUpdatedPhotos.putParcelable(String.valueOf(mPhotoRawContactId), uri);
        getContent().updatePhoto(uri);
    }

    public void setPrimaryPhoto(CompactPhotoSelectionFragment.Photo photo) {
        getContent().setPrimaryPhoto(photo);

        // Update the photo ID we will try to match when selecting the photo to display
        mPhotoId = photo.photoId;
    }

    @Override
    public void onNameFieldChanged(long rawContactId, ValuesDelta valuesDelta) {
        final Activity activity = getActivity();
        if (activity == null || activity.isFinishing()) {
            return;
        }
        acquireAggregationSuggestions(activity, rawContactId, valuesDelta);
    }

    @Override
    public void onRebindEditorsForNewContact(RawContactDelta oldState,
            AccountWithDataSet oldAccount, AccountWithDataSet newAccount) {
        mNewContactAccountChanged = true;
        mAccountWithDataSet = newAccount;
        rebindEditorsForNewContact(oldState, oldAccount, newAccount);
    }

    @Override
    public void onBindEditorsFailed() {
        final Activity activity = getActivity();
        if (activity != null && !activity.isFinishing()) {
            Toast.makeText(activity, R.string.compact_editor_failed_to_load,
                    Toast.LENGTH_SHORT).show();
            activity.setResult(Activity.RESULT_CANCELED);
            activity.finish();
        }
    }

    @Override
    public void onEditorsBound() {
        getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupsLoaderListener);
    }

    @Override
    public void onPhotoEditorViewClicked() {
        if (isEditingMultipleRawContacts()) {
            final ArrayList<CompactPhotoSelectionFragment.Photo> photos = getContent().getPhotos();
            if (photos.size() > 1) {
                updatePrimaryForSelection(photos);
                // For aggregate contacts, the user may select a new super primary photo from among
                // the (non-default) raw contact photos, or source a new photo.
                getEditorActivity().selectPhoto(photos, getPhotoMode());
                return;
            }
        }
        // For contacts composed of a single writable raw contact, or raw contacts have no more
        // than 1 photo, clicking the photo view simply opens the source photo dialog
        getEditorActivity().changePhoto(getPhotoMode());
    }

    // This method override photo's primary flag based on photoId and set the photo currently
    // shown in the editor to be the new primary no matter how many primary photos there are in
    // the photo picker. This is because the photos returned by "getPhoto" may contain 0, 1,
    // or 2+ primary photos and when we link contacts in the editor, the photos returned may change.
    // We need to put check mark on the photo currently shown in editor, so we override "primary".
    // This doesn't modify anything in the database,so there would be no pending changes.
    private void updatePrimaryForSelection(ArrayList<CompactPhotoSelectionFragment.Photo> photos) {
        for (CompactPhotoSelectionFragment.Photo photo : photos) {
            if (photo.photoId == mPhotoId) {
                photo.primary = true;
            } else {
                photo.primary = false;
            }
            updateContentDescription(photo);
        }
    }

    private void updateContentDescription(CompactPhotoSelectionFragment.Photo photo) {
        if (!TextUtils.isEmpty(photo.accountType)) {
            photo.contentDescription = getResources().getString(photo.primary ?
                            R.string.photo_view_description_checked :
                            R.string.photo_view_description_not_checked,
                    photo.accountType, photo.accountName);
            photo.contentDescriptionChecked = getResources().getString(
                    R.string.photo_view_description_checked,
                    photo.accountType, photo.accountName);
        } else {
            photo.contentDescription = getResources().getString(photo.primary ?
                    R.string.photo_view_description_checked_no_info :
                    R.string.photo_view_description_not_checked_no_info);
            photo.contentDescriptionChecked = getResources().getString(
                    R.string.photo_view_description_checked_no_info);
        }
    }

    @Override
    public void onRawContactSelected(Uri uri, long rawContactId, boolean isReadOnly) {
        final Activity activity = getActivity();
        if (activity != null && !activity.isFinishing()) {
            final Intent intent = EditorIntents.createEditContactIntentForRawContact(
                    activity, uri, rawContactId, isReadOnly);
            activity.startActivity(intent);
        }
    }

    @Override
    public Bundle getUpdatedPhotos() {
        return mUpdatedPhotos;
    }

    private int getPhotoMode() {
        if (getContent().isWritablePhotoSet()) {
            return isEditingMultipleRawContacts()
                    ? PhotoActionPopup.Modes.MULTIPLE_WRITE_ABLE_PHOTOS
                    : PhotoActionPopup.Modes.WRITE_ABLE_PHOTO;
        }
        return PhotoActionPopup.Modes.NO_PHOTO;
    }

    private CompactContactEditorActivity getEditorActivity() {
        return (CompactContactEditorActivity) getActivity();
    }

    private CompactRawContactsEditorView getContent() {
        return (CompactRawContactsEditorView) mContent;
    }
}
