// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.sync.ui;

import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AlertDialog;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.CheckedTextView;
import android.widget.ListView;
import android.widget.TextView;

import org.chromium.base.BuildInfo;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.util.IntentUtils;
import org.chromium.components.sync.PassphraseType;
import org.chromium.ui.text.SpanApplier;
import org.chromium.ui.text.SpanApplier.SpanInfo;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * Dialog to ask the user select what type of password to use for encryption.
 */
public class PassphraseTypeDialogFragment extends DialogFragment implements
        DialogInterface.OnClickListener, OnItemClickListener {
    private static final String TAG = "PassphraseTypeDialogFragment";

    interface Listener {
        void onPassphraseTypeSelected(PassphraseType type);
    }

    private String[] getDisplayNames(List<PassphraseType> passphraseTypes) {
        String[] displayNames = new String[passphraseTypes.size()];
        for (int i = 0; i < displayNames.length; i++) {
            displayNames[i] = textForPassphraseType(passphraseTypes.get(i));
        }
        return displayNames;
    }

    private String textForPassphraseType(PassphraseType type) {
        switch (type) {
            case IMPLICIT_PASSPHRASE:  // Intentional fall through.
            case KEYSTORE_PASSPHRASE:
                return getString(R.string.sync_passphrase_type_keystore);
            case FROZEN_IMPLICIT_PASSPHRASE:
                String passphraseDate = getPassphraseDateStringFromArguments();
                String frozenPassphraseString = getString(R.string.sync_passphrase_type_frozen);
                return String.format(frozenPassphraseString, passphraseDate);
            case CUSTOM_PASSPHRASE:
                return getString(R.string.sync_passphrase_type_custom);
            default:
                return "";
        }
    }

    private Adapter createAdapter(PassphraseType currentType) {
        List<PassphraseType> passphraseTypes =
                new ArrayList<PassphraseType>(currentType.getVisibleTypes());
        return new Adapter(passphraseTypes, getDisplayNames(passphraseTypes));
    }

    /**
     * The adapter for our ListView; only visible for testing purposes.
     */
    @VisibleForTesting
    public class Adapter extends ArrayAdapter<String> {

        private final List<PassphraseType> mPassphraseTypes;

        /**
         * Do not call this constructor directly. Instead use
         * {@link PassphraseTypeDialogFragment#createAdapter}.
         */
        private Adapter(List<PassphraseType> passphraseTypes, String[] displayStrings) {
            super(getActivity(), R.layout.passphrase_type_item, displayStrings);
            mPassphraseTypes = passphraseTypes;
        }

        @Override
        public boolean hasStableIds() {
            return true;
        }

        @Override
        public long getItemId(int position) {
            return getType(position).internalValue();
        }

        public PassphraseType getType(int position) {
            return mPassphraseTypes.get(position);
        }

        public int getPositionForType(PassphraseType type) {
            return mPassphraseTypes.indexOf(type);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            CheckedTextView view = (CheckedTextView) super.getView(position, convertView, parent);
            PassphraseType positionType = getType(position);
            PassphraseType currentType = getCurrentTypeFromArguments();
            Set<PassphraseType> allowedTypes =
                    currentType.getAllowedTypes(getIsEncryptEverythingAllowedFromArguments());

            // Set the item to checked it if it is the currently selected encryption type.
            view.setChecked(positionType == currentType);
            // Allow user to click on enabled types for the current type.
            view.setEnabled(allowedTypes.contains(positionType));
            return view;
        }
    }

    /**
     * This argument should contain a single value of type {@link PassphraseType}.
     */
    private static final String ARG_CURRENT_TYPE = "arg_current_type";

    private static final String ARG_PASSPHRASE_TIME = "arg_passphrase_time";

    private static final String ARG_IS_ENCRYPT_EVERYTHING_ALLOWED =
            "arg_is_encrypt_everything_allowed";

    static PassphraseTypeDialogFragment create(
            PassphraseType currentType, long passphraseTime, boolean isEncryptEverythingAllowed) {
        PassphraseTypeDialogFragment dialog = new PassphraseTypeDialogFragment();
        Bundle args = new Bundle();
        args.putParcelable(ARG_CURRENT_TYPE, currentType);
        args.putLong(ARG_PASSPHRASE_TIME, passphraseTime);
        args.putBoolean(ARG_IS_ENCRYPT_EVERYTHING_ALLOWED, isEncryptEverythingAllowed);
        dialog.setArguments(args);
        return dialog;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        LayoutInflater inflater = getActivity().getLayoutInflater();
        View v = inflater.inflate(R.layout.sync_passphrase_types, null);

        // Configure the passphrase type list
        ListView list = (ListView) v.findViewById(R.id.passphrase_types);
        Adapter adapter = createAdapter(getCurrentTypeFromArguments());
        list.setAdapter(adapter);
        list.setId(R.id.passphrase_type_list);
        list.setOnItemClickListener(this);
        list.setDividerHeight(0);
        PassphraseType currentType = getCurrentTypeFromArguments();
        list.setSelection(adapter.getPositionForType(currentType));

        // Configure the hint to reset the passphrase settings
        // Only show this hint if encryption has been set to use sync passphrase
        if (currentType == PassphraseType.CUSTOM_PASSPHRASE) {
            TextView instructionsView = (TextView) v.findViewById(R.id.reset_sync_text);
            instructionsView.setVisibility(View.VISIBLE);
            instructionsView.setMovementMethod(LinkMovementMethod.getInstance());
            instructionsView.setText(getResetText());
        }

        // Create and return the dialog
        return new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme)
                .setNegativeButton(R.string.cancel, this)
                .setTitle(R.string.sync_passphrase_type_title)
                .setView(v)
                .create();
    }

    private SpannableString getResetText() {
        final Context context = getActivity();
        return SpanApplier.applySpans(
                context.getString(R.string.sync_passphrase_encryption_reset_instructions),
                new SpanInfo("<resetlink>", "</resetlink>", new ClickableSpan() {
                    @Override
                    public void onClick(View view) {
                        Uri syncDashboardUrl = Uri.parse(
                                context.getText(R.string.sync_dashboard_url).toString());
                        Intent intent = new Intent(Intent.ACTION_VIEW, syncDashboardUrl);
                        intent.setPackage(BuildInfo.getPackageName(context));
                        IntentUtils.safePutBinderExtra(
                                intent, CustomTabsIntent.EXTRA_SESSION, null);
                        context.startActivity(intent);
                    }
                }));
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_NEGATIVE) {
            dismiss();
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long typeId) {
        PassphraseType currentType = getCurrentTypeFromArguments();
        // We know this conversion from long to int is safe, because it represents very small
        // enum values.
        PassphraseType type = PassphraseType.fromInternalValue((int) typeId);
        boolean isEncryptEverythingAllowed = getIsEncryptEverythingAllowedFromArguments();
        if (currentType.getAllowedTypes(isEncryptEverythingAllowed).contains(type)) {
            if (typeId != currentType.internalValue()) {
                Listener listener = (Listener) getTargetFragment();
                listener.onPassphraseTypeSelected(type);
            }
            dismiss();
        }
    }

    @VisibleForTesting
    public PassphraseType getCurrentTypeFromArguments() {
        PassphraseType currentType = getArguments().getParcelable(ARG_CURRENT_TYPE);
        if (currentType == null) {
            throw new IllegalStateException("Unable to find argument with current type.");
        }
        return currentType;
    }

    private String getPassphraseDateStringFromArguments() {
        long passphraseTime = getArguments().getLong(ARG_PASSPHRASE_TIME);
        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
        return df.format(new Date(passphraseTime));
    }

    private boolean getIsEncryptEverythingAllowedFromArguments() {
        return getArguments().getBoolean(ARG_IS_ENCRYPT_EVERYTHING_ALLOWED);
    }
}