/*
 * Copyright (C) 2011 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.server.telecom;

// TODO: Needed for move to system service: import com.android.internal.R;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.telecom.Connection;
import android.telecom.Log;
import android.telecom.Response;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.SubscriptionManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

/**
 * Helper class to manage the "Respond via Message" feature for incoming calls.
 */
public class RespondViaSmsManager extends CallsManagerListenerBase {
    private static final String ACTION_MESSAGE_SENT = "com.android.server.telecom.MESSAGE_SENT";

    private static final class MessageSentReceiver extends BroadcastReceiver {
        private final String mContactName;
        private final int mNumMessageParts;
        private int mNumMessagesSent = 0;
        MessageSentReceiver(String contactName, int numMessageParts) {
            mContactName = contactName;
            mNumMessageParts = numMessageParts;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            if (getResultCode() == Activity.RESULT_OK) {
                mNumMessagesSent++;
                if (mNumMessagesSent == mNumMessageParts) {
                    showMessageResultToast(mContactName, context, true);
                    context.unregisterReceiver(this);
                }
            } else {
                context.unregisterReceiver(this);
                showMessageResultToast(mContactName, context, false);
                Log.w(RespondViaSmsManager.class.getSimpleName(),
                        "Message failed with error %s", getResultCode());
            }
        }
    }

    private final CallsManager mCallsManager;
    private final TelecomSystem.SyncRoot mLock;

    public RespondViaSmsManager(CallsManager callsManager, TelecomSystem.SyncRoot lock) {
        mCallsManager = callsManager;
        mLock = lock;
    }

    /**
     * Read the (customizable) canned responses from SharedPreferences,
     * or from defaults if the user has never actually brought up
     * the Settings UI.
     *
     * The interface of this method is asynchronous since it does disk I/O.
     *
     * @param response An object to receive an async reply, which will be called from
     *                 the main thread.
     * @param context The context.
     */
    public void loadCannedTextMessages(final Response<Void, List<String>> response,
            final Context context) {
        new Thread() {
            @Override
            public void run() {
                Log.d(RespondViaSmsManager.this, "loadCannedResponses() starting");

                // This function guarantees that QuickResponses will be in our
                // SharedPreferences with the proper values considering there may be
                // old QuickResponses in Telephony pre L.
                QuickResponseUtils.maybeMigrateLegacyQuickResponses(context);

                final SharedPreferences prefs = context.getSharedPreferences(
                        QuickResponseUtils.SHARED_PREFERENCES_NAME,
                        Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS);
                final Resources res = context.getResources();

                final ArrayList<String> textMessages = new ArrayList<>(
                        QuickResponseUtils.NUM_CANNED_RESPONSES);

                // Where the user has changed a quick response back to the same text as the
                // original text, clear the shared pref.  This ensures we always load the resource
                // in the current active language.
                QuickResponseUtils.maybeResetQuickResponses(context, prefs);

                // Note the default values here must agree with the corresponding
                // android:defaultValue attributes in respond_via_sms_settings.xml.
                textMessages.add(0, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1,
                        res.getString(R.string.respond_via_sms_canned_response_1)));
                textMessages.add(1, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_2,
                        res.getString(R.string.respond_via_sms_canned_response_2)));
                textMessages.add(2, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_3,
                        res.getString(R.string.respond_via_sms_canned_response_3)));
                textMessages.add(3, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_4,
                        res.getString(R.string.respond_via_sms_canned_response_4)));

                Log.d(RespondViaSmsManager.this,
                        "loadCannedResponses() completed, found responses: %s",
                        textMessages.toString());

                synchronized (mLock) {
                    response.onResult(null, textMessages);
                }
            }
        }.start();
    }

    @Override
    public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
        if (rejectWithMessage
                && call.getHandle() != null
                && !call.can(Connection.CAPABILITY_CAN_SEND_RESPONSE_VIA_CONNECTION)) {
            int subId = mCallsManager.getPhoneAccountRegistrar().getSubscriptionIdForPhoneAccount(
                    call.getTargetPhoneAccount());
            rejectCallWithMessage(call.getContext(), call.getHandle().getSchemeSpecificPart(),
                    textMessage, subId, call.getName());
        }
    }

    private static void showMessageResultToast(final String phoneNumber,
            final Context context, boolean success) {
        // ...and show a brief confirmation to the user (since
        // otherwise it's hard to be sure that anything actually
        // happened.)
        final Resources res = context.getResources();
        final String formatString = res.getString(success
                ? R.string.respond_via_sms_confirmation_format
                : R.string.respond_via_sms_failure_format);
        final String confirmationMsg = String.format(formatString, phoneNumber);
        int startingPosition = confirmationMsg.indexOf(phoneNumber);
        int endingPosition = startingPosition + phoneNumber.length();

        Spannable styledConfirmationMsg = new SpannableString(confirmationMsg);
        PhoneNumberUtils.addTtsSpan(styledConfirmationMsg, startingPosition, endingPosition);
        Toast.makeText(context, styledConfirmationMsg,
                Toast.LENGTH_LONG).show();

        // TODO: If the device is locked, this toast won't actually ever
        // be visible!  (That's because we're about to dismiss the call
        // screen, which means that the device will return to the
        // keyguard.  But toasts aren't visible on top of the keyguard.)
        // Possible fixes:
        // (1) Is it possible to allow a specific Toast to be visible
        //     on top of the keyguard?
        // (2) Artificially delay the dismissCallScreen() call by 3
        //     seconds to allow the toast to be seen?
        // (3) Don't use a toast at all; instead use a transient state
        //     of the InCallScreen (perhaps via the InCallUiState
        //     progressIndication feature), and have that state be
        //     visible for 3 seconds before calling dismissCallScreen().
    }

    /**
     * Reject the call with the specified message. If message is null this call is ignored.
     */
    private void rejectCallWithMessage(Context context, String phoneNumber, String textMessage,
            int subId, String contactName) {
        if (TextUtils.isEmpty(textMessage)) {
            Log.w(RespondViaSmsManager.this, "Couldn't send SMS message: empty text message. ");
            return;
        }
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            Log.w(RespondViaSmsManager.this, "Couldn't send SMS message: Invalid SubId: " +
                    subId);
            return;
        }

        SmsManager smsManager = SmsManager.getSmsManagerForSubscriptionId(subId);
        try {
            ArrayList<String> messageParts = smsManager.divideMessage(textMessage);
            ArrayList<PendingIntent> sentIntents = new ArrayList<>(messageParts.size());
            for (int i = 0; i < messageParts.size(); i++) {
                Intent intent = new Intent(ACTION_MESSAGE_SENT);
                PendingIntent pendingIntent = PendingIntent.getBroadcast(context, i, intent,
                        PendingIntent.FLAG_ONE_SHOT);
                sentIntents.add(pendingIntent);
            }
            MessageSentReceiver receiver = new MessageSentReceiver(
                    !TextUtils.isEmpty(contactName) ? contactName : phoneNumber,
                    messageParts.size());
            context.registerReceiver(receiver, new IntentFilter(ACTION_MESSAGE_SENT));
            smsManager.sendMultipartTextMessage(phoneNumber, null, messageParts,
                    sentIntents/*sentIntent*/, null /*deliveryIntent*/, context.getOpPackageName(),
                    context.getAttributionTag());
        } catch (IllegalArgumentException e) {
            Log.w(RespondViaSmsManager.this, "Couldn't send SMS message: " +
                    e.getMessage());
        }
    }
}
