/*
 * 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.android.emergency.preferences;

import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import android.telecom.TelecomManager;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import com.android.emergency.CircleFramedDrawable;
import com.android.emergency.EmergencyContactManager;
import com.android.emergency.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;

import java.util.List;


/**
 * A {@link Preference} to display or call a contact using the specified URI string.
 */
public class ContactPreference extends Preference {

    private static final String TAG = "ContactPreference";

    static final ContactFactory DEFAULT_CONTACT_FACTORY = new ContactFactory() {
        @Override
        public EmergencyContactManager.Contact getContact(Context context, Uri phoneUri) {
            return EmergencyContactManager.getContact(context, phoneUri);
        }
    };

    private final ContactFactory mContactFactory;
    private EmergencyContactManager.Contact mContact;
    @Nullable private RemoveContactPreferenceListener mRemoveContactPreferenceListener;
    @Nullable private AlertDialog mRemoveContactDialog;

    /**
     * Listener for removing a contact.
     */
    public interface RemoveContactPreferenceListener {
        /**
         * Callback to remove a contact preference.
         */
        void onRemoveContactPreference(ContactPreference preference);
    }

    /**
     * Interface for getting a contact for a phone number Uri.
     */
    public interface ContactFactory {
        /**
         * Gets a {@link EmergencyContactManager.Contact} for a phone {@link Uri}.
         *
         * @param context The context to use.
         * @param phoneUri The phone uri.
         * @return a contact for the given phone uri.
         */
        EmergencyContactManager.Contact getContact(Context context, Uri phoneUri);
    }

    public ContactPreference(Context context, AttributeSet attributes) {
        super(context, attributes);
        mContactFactory = DEFAULT_CONTACT_FACTORY;
    }

    /**
     * Instantiates a ContactPreference that displays an emergency contact, taking in a Context and
     * the Uri.
     */
    public ContactPreference(Context context, @NonNull Uri phoneUri) {
        this(context, phoneUri, DEFAULT_CONTACT_FACTORY);
    }

    @VisibleForTesting
    ContactPreference(Context context, @NonNull Uri phoneUri,
            @NonNull ContactFactory contactFactory) {
        super(context);
        mContactFactory = contactFactory;
        setOrder(DEFAULT_ORDER);

        setPhoneUri(phoneUri);

        setWidgetLayoutResource(R.layout.preference_user_action_widget);
        setPersistent(false);
    }

    public void setPhoneUri(@NonNull Uri phoneUri) {
        if (mContact != null && !phoneUri.equals(mContact.getPhoneUri()) &&
                mRemoveContactDialog != null) {
            mRemoveContactDialog.dismiss();
        }
        mContact = mContactFactory.getContact(getContext(), phoneUri);

        setTitle(mContact.getName());
        setKey(mContact.getPhoneUri().toString());
        String summary = mContact.getPhoneType() == null ?
                mContact.getPhoneNumber() :
                String.format(
                        getContext().getResources().getString(R.string.phone_type_and_phone_number),
                        mContact.getPhoneType(),
                        BidiFormatter.getInstance().unicodeWrap(mContact.getPhoneNumber(),
                                TextDirectionHeuristics.LTR));
        setSummary(summary);

        // Update the message to show the correct name.
        if (mRemoveContactDialog != null) {
            mRemoveContactDialog.setMessage(
                    String.format(getContext().getString(R.string.remove_contact),
                            mContact.getName()));
        }

        //TODO: Consider doing the following in a non-UI thread.
        Drawable icon;
        if (mContact.getPhoto() != null) {
            icon = new CircleFramedDrawable(mContact.getPhoto(),
                    (int) getContext().getResources().getDimension(R.dimen.circle_avatar_size));
        } else {
            icon = getContext().getDrawable(R.drawable.ic_account_circle_filled_24dp);
        }
        setIcon(icon);
    }

    /** Listener to be informed when a contact preference should be deleted. */
    public void setRemoveContactPreferenceListener(
            RemoveContactPreferenceListener removeContactListener) {
        mRemoveContactPreferenceListener = removeContactListener;
        if (mRemoveContactPreferenceListener == null) {
            mRemoveContactDialog = null;
            return;
        }
        if (mRemoveContactDialog != null) {
            return;
        }
        // Create the remove contact dialog
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setNegativeButton(getContext().getString(R.string.cancel), null);
        builder.setPositiveButton(getContext().getString(R.string.remove),
                new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface,
                                        int which) {
                        if (mRemoveContactPreferenceListener != null) {
                            mRemoveContactPreferenceListener
                                    .onRemoveContactPreference(ContactPreference.this);
                        }
                    }
                });
        builder.setMessage(String.format(getContext().getString(R.string.remove_contact),
                mContact.getName()));
        mRemoveContactDialog = builder.create();
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);
        View deleteContactIcon = holder.findViewById(R.id.delete_contact);
        View callContactIcon = holder.findViewById(R.id.call_contact);
        if (mRemoveContactPreferenceListener == null) {
            // Default icon is delete, change icon to phone when ContactPreference binding
            // ViewEmergencyContactsFragment.
            deleteContactIcon.setVisibility(View.GONE);
            callContactIcon.setVisibility(View.VISIBLE);
        } else {
            deleteContactIcon.setOnClickListener((View view) -> {
                showRemoveContactDialog(null);
            });
        }
    }

    public Uri getPhoneUri() {
        return mContact.getPhoneUri();
    }

    @VisibleForTesting
    EmergencyContactManager.Contact getContact() {
        return mContact;
    }

    @VisibleForTesting
    AlertDialog getRemoveContactDialog() {
        return mRemoveContactDialog;
    }

    /**
     * Calls the contact.
     */
    public void callContact() {
        // Use TelecomManager to place the call; this APK has CALL_PRIVILEGED permission so it will
        // be able to call emergency numbers.
        TelecomManager tm = (TelecomManager) getContext().getSystemService(Context.TELECOM_SERVICE);
        tm.placeCall(Uri.parse("tel:" + mContact.getPhoneNumber()), null);
        MetricsLogger.action(getContext(), MetricsEvent.ACTION_CALL_EMERGENCY_CONTACT);
    }

    /**
     * Displays a contact card for the contact.
     */
    public void displayContact() {
        Intent displayIntent = new Intent(Intent.ACTION_VIEW)
                .setData(mContact.getContactLookupUri())
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        try {
            getContext().startActivity(displayIntent);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(getContext(),
                           getContext().getString(R.string.fail_display_contact),
                           Toast.LENGTH_LONG).show();
            Log.w(TAG, "No contact app available to display the contact", e);
            return;
        }

    }

    /** Shows the dialog to remove the contact, restoring it from {@code state} if it's not null. */
    private void showRemoveContactDialog(Bundle state) {
        if (mRemoveContactDialog == null) {
            return;
        }
        if (state != null) {
            mRemoveContactDialog.onRestoreInstanceState(state);
        }
        mRemoveContactDialog.show();
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Parcelable superState = super.onSaveInstanceState();
        if (mRemoveContactDialog == null || !mRemoveContactDialog.isShowing()) {
            return superState;
        }
        final SavedState myState = new SavedState(superState);
        myState.isDialogShowing = true;
        myState.dialogBundle = mRemoveContactDialog.onSaveInstanceState();
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state == null || !state.getClass().equals(SavedState.class)) {
            // Didn't save state for us in onSaveInstanceState
            super.onRestoreInstanceState(state);
            return;
        }
        SavedState myState = (SavedState) state;
        super.onRestoreInstanceState(myState.getSuperState());
        if (myState.isDialogShowing) {
            showRemoveContactDialog(myState.dialogBundle);
        }
    }

    private static class SavedState extends BaseSavedState {
        boolean isDialogShowing;
        Bundle dialogBundle;

        public SavedState(Parcel source) {
            super(source);
            isDialogShowing = source.readInt() == 1;
            dialogBundle = source.readBundle();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(isDialogShowing ? 1 : 0);
            dest.writeBundle(dialogBundle);
        }

        public SavedState(Parcelable superState) {
            super(superState);
        }

        public static final Parcelable.Creator<SavedState> CREATOR =
                new Parcelable.Creator<SavedState>() {
                    public SavedState createFromParcel(Parcel in) {
                        return new SavedState(in);
                    }

                    public SavedState[] newArray(int size) {
                        return new SavedState[size];
                    }
                };
    }
}
