/*
 * 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.packageinstaller.permission.ui.handheld;

import android.content.Context;
import android.graphics.drawable.Icon;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;

import com.android.packageinstaller.R;
import com.android.packageinstaller.permission.ui.ButtonBarLayout;
import com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler;
import com.android.packageinstaller.permission.ui.ManualLayoutFrame;

public final class GrantPermissionsViewHandlerImpl
        implements GrantPermissionsViewHandler, OnClickListener {

    public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
    public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
    public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
    public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON";
    public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
    public static final String ARG_GROUP_SHOW_DO_NOT_ASK = "ARG_GROUP_SHOW_DO_NOT_ASK";
    public static final String ARG_GROUP_DO_NOT_ASK_CHECKED = "ARG_GROUP_DO_NOT_ASK_CHECKED";

    // Animation parameters.
    private static final long OUT_DURATION = 200;
    private static final long IN_DURATION = 300;

    private final Context mContext;

    private ResultListener mResultListener;

    private String mGroupName;
    private int mGroupCount;
    private int mGroupIndex;
    private Icon mGroupIcon;
    private CharSequence mGroupMessage;
    private boolean mShowDonNotAsk;
    private boolean mDoNotAskChecked;

    private ImageView mIconView;
    private TextView mCurrentGroupView;
    private TextView mMessageView;
    private CheckBox mDoNotAskCheckbox;
    private Button mAllowButton;

    private ManualLayoutFrame mRootView;

    // Needed for animation
    private ViewGroup mDescContainer;
    private ViewGroup mCurrentDesc;
    private ViewGroup mDialogContainer;
    private ButtonBarLayout mButtonBar;

    public GrantPermissionsViewHandlerImpl(Context context) {
        mContext = context;
    }

    @Override
    public GrantPermissionsViewHandlerImpl setResultListener(ResultListener listener) {
        mResultListener = listener;
        return this;
    }

    @Override
    public void saveInstanceState(Bundle arguments) {
        arguments.putString(ARG_GROUP_NAME, mGroupName);
        arguments.putInt(ARG_GROUP_COUNT, mGroupCount);
        arguments.putInt(ARG_GROUP_INDEX, mGroupIndex);
        arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon);
        arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage);
        arguments.putBoolean(ARG_GROUP_SHOW_DO_NOT_ASK, mShowDonNotAsk);
        arguments.putBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED, mDoNotAskCheckbox.isChecked());
    }

    @Override
    public void loadInstanceState(Bundle savedInstanceState) {
        mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
        mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE);
        mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON);
        mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
        mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
        mShowDonNotAsk = savedInstanceState.getBoolean(ARG_GROUP_SHOW_DO_NOT_ASK);
        mDoNotAskChecked = savedInstanceState.getBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED);
    }

    @Override
    public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
            CharSequence message, boolean showDonNotAsk) {
        mGroupName = groupName;
        mGroupCount = groupCount;
        mGroupIndex = groupIndex;
        mGroupIcon = icon;
        mGroupMessage = message;
        mShowDonNotAsk = showDonNotAsk;
        mDoNotAskChecked = false;
        // If this is a second (or later) permission and the views exist, then animate.
        if (mIconView != null) {
            if (mGroupIndex > 0) {
                // The first message will be announced as the title of the activity, all others
                // we need to announce ourselves.
                mDescContainer.announceForAccessibility(message);
                animateToPermission();
            } else {
                updateDescription();
                updateGroup();
                updateDoNotAskCheckBox();
            }
        }
    }

    public void onConfigurationChanged() {
        mRootView.onConfigurationChanged();
    }

    private void animateOldContent(Runnable callback) {
        // Fade out old description group and scale out the icon for it.
        Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
                android.R.interpolator.fast_out_linear_in);

        // Icon scale to zero
        mIconView.animate()
                .scaleX(0)
                .scaleY(0)
                .setDuration(OUT_DURATION)
                .setInterpolator(interpolator)
                .start();

        // Description fade out
        mCurrentDesc.animate()
                .alpha(0)
                .setDuration(OUT_DURATION)
                .setInterpolator(interpolator)
                .withEndAction(callback)
                .start();

        // Checkbox fade out if needed
        if (!mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE) {
            mDoNotAskCheckbox.animate()
                    .alpha(0)
                    .setDuration(OUT_DURATION)
                    .setInterpolator(interpolator)
                    .start();
        }
    }

    private void attachNewContent(final Runnable callback) {
        mCurrentDesc = (ViewGroup) LayoutInflater.from(mContext).inflate(
                R.layout.permission_description, mDescContainer, false);
        mDescContainer.removeAllViews();
        mDescContainer.addView(mCurrentDesc);

        mDialogContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                @Override
                public void onLayoutChange(View v, int left, int top, int right, int bottom,
                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    mDialogContainer.removeOnLayoutChangeListener(this);

                    // Prepare new content to the right to be moved in
                    final int containerWidth = mDescContainer.getWidth();
                    mCurrentDesc.setTranslationX(containerWidth);

                    // How much scale for the dialog to appear the same?
                    final int oldDynamicHeight = oldBottom - oldTop - mButtonBar.getHeight();
                    final float scaleY = (float) oldDynamicHeight / mDescContainer.getHeight();

                    // How much to translate for the dialog to appear the same?
                    final int translationCompensatingScale = (int) (scaleY
                            * mDescContainer.getHeight() - mDescContainer.getHeight()) / 2;
                    final int translationY = (oldTop - top) + translationCompensatingScale;

                    // Animate to the current layout
                    mDescContainer.setScaleY(scaleY);
                    mDescContainer.setTranslationY(translationY);
                    mDescContainer.animate()
                            .translationY(0)
                            .scaleY(1.0f)
                            .setInterpolator(AnimationUtils.loadInterpolator(mContext,
                                    android.R.interpolator.linear_out_slow_in))
                            .setDuration(IN_DURATION)
                            .withEndAction(callback)
                            .start();
                }
            }
        );

        mMessageView = (TextView) mCurrentDesc.findViewById(R.id.permission_message);
        mIconView = (ImageView) mCurrentDesc.findViewById(R.id.permission_icon);

        final boolean doNotAskWasShown = mDoNotAskCheckbox.getVisibility() == View.VISIBLE;

        updateDescription();
        updateGroup();
        updateDoNotAskCheckBox();

        if (!doNotAskWasShown && mShowDonNotAsk) {
            mDoNotAskCheckbox.setAlpha(0);
        }
    }

    private void animateNewContent() {
        Interpolator interpolator = AnimationUtils.loadInterpolator(mContext,
                android.R.interpolator.linear_out_slow_in);

        // Description slide in
        mCurrentDesc.animate()
                .translationX(0)
                .setDuration(IN_DURATION)
                .setInterpolator(interpolator)
                .start();

        // Checkbox fade in if needed
        if (mShowDonNotAsk && mDoNotAskCheckbox.getVisibility() == View.VISIBLE
                && mDoNotAskCheckbox.getAlpha() < 1.0f) {
            mDoNotAskCheckbox.setAlpha(0);
            mDoNotAskCheckbox.animate()
                    .alpha(1.0f)
                    .setDuration(IN_DURATION)
                    .setInterpolator(interpolator)
                    .start();
        }
    }

    private void animateToPermission() {
        // Remove the old content
        animateOldContent(new Runnable() {
            @Override
            public void run() {
                // Add the new content
                attachNewContent(new Runnable() {
                    @Override
                    public void run() {
                        // Animate the new content
                        animateNewContent();
                    }
                });
            }
        });
    }

    @Override
    public View createView() {
        mRootView = (ManualLayoutFrame) LayoutInflater.from(mContext)
                .inflate(R.layout.grant_permissions, null);
        mButtonBar = (ButtonBarLayout) mRootView.findViewById(R.id.button_group);
        mButtonBar.setAllowStacking(true);
        mMessageView = (TextView) mRootView.findViewById(R.id.permission_message);
        mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon);
        mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text);
        mDoNotAskCheckbox = (CheckBox) mRootView.findViewById(R.id.do_not_ask_checkbox);
        mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);

        mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container);
        mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container);
        mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root);

        mAllowButton.setOnClickListener(this);
        mRootView.findViewById(R.id.permission_deny_button).setOnClickListener(this);
        mDoNotAskCheckbox.setOnClickListener(this);

        if (mGroupName != null) {
            updateDescription();
            updateGroup();
            updateDoNotAskCheckBox();
        }

        return mRootView;
    }

    @Override
    public void updateWindowAttributes(LayoutParams outLayoutParams) {
        // No-op
    }

    private void updateDescription() {
        mIconView.setImageDrawable(mGroupIcon.loadDrawable(mContext));
        mMessageView.setText(mGroupMessage);
    }

    private void updateGroup() {
        if (mGroupCount > 1) {
            mCurrentGroupView.setVisibility(View.VISIBLE);
            mCurrentGroupView.setText(mContext.getString(R.string.current_permission_template,
                    mGroupIndex + 1, mGroupCount));
        } else {
            mCurrentGroupView.setVisibility(View.INVISIBLE);
        }
    }

    private void updateDoNotAskCheckBox() {
        if (mShowDonNotAsk) {
            mDoNotAskCheckbox.setVisibility(View.VISIBLE);
            mDoNotAskCheckbox.setOnClickListener(this);
            mDoNotAskCheckbox.setChecked(mDoNotAskChecked);
        } else {
            mDoNotAskCheckbox.setVisibility(View.GONE);
            mDoNotAskCheckbox.setOnClickListener(null);
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.permission_allow_button:
                if (mResultListener != null) {
                    view.clearAccessibilityFocus();
                    mResultListener.onPermissionGrantResult(mGroupName, true, false);
                }
                break;
            case R.id.permission_deny_button:
                mAllowButton.setEnabled(true);
                if (mResultListener != null) {
                    view.clearAccessibilityFocus();
                    mResultListener.onPermissionGrantResult(mGroupName, false,
                            mShowDonNotAsk && mDoNotAskCheckbox.isChecked());
                }
                break;
            case R.id.do_not_ask_checkbox:
                mAllowButton.setEnabled(!mDoNotAskCheckbox.isChecked());
                break;
        }
    }

    @Override
    public void onBackPressed() {
        if (mResultListener != null) {
            final boolean doNotAskAgain = mDoNotAskCheckbox.isChecked();
            mResultListener.onPermissionGrantResult(mGroupName, false, doNotAskAgain);
        }
    }
}
