/*
 * Copyright (C) 2017 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.launcher3.dragndrop;

import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;

import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.appwidget.AppWidgetManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps.PinItemRequest;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.DragShadowBuilder;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;

import com.android.launcher3.BaseActivity;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;

@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {

    private static final int SHADOW_SIZE = 10;

    private static final int REQUEST_BIND_APPWIDGET = 1;
    private static final String STATE_EXTRA_WIDGET_ID = "state.widget.id";

    private final PointF mLastTouchPos = new PointF();

    private PinItemRequest mRequest;
    private LauncherAppState mApp;
    private InvariantDeviceProfile mIdp;

    private LivePreviewWidgetCell mWidgetCell;

    // Widget request specific options.
    private LauncherAppWidgetHost mAppWidgetHost;
    private AppWidgetManagerCompat mAppWidgetManager;
    private PendingAddWidgetInfo mPendingWidgetInfo;
    private int mPendingBindWidgetId;
    private Bundle mWidgetOptions;

    private boolean mFinishOnPause = false;
    private InstantAppResolver mInstantAppResolver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mRequest = LauncherAppsCompatVO.getPinItemRequest(getIntent());
        if (mRequest == null) {
            finish();
            return;
        }

        mApp = LauncherAppState.getInstance(this);
        mIdp = mApp.getInvariantDeviceProfile();
        mInstantAppResolver = InstantAppResolver.newInstance(this);

        // Use the application context to get the device profile, as in multiwindow-mode, the
        // confirmation activity might be rotated.
        mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());

        setContentView(R.layout.add_item_confirmation_activity);
        mWidgetCell = findViewById(R.id.widget_cell);

        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
            setupShortcut();
        } else {
            if (!setupWidget()) {
                // TODO: show error toast?
                finish();
            }
        }

        mWidgetCell.setOnTouchListener(this);
        mWidgetCell.setOnLongClickListener(this);

        // savedInstanceState is null when the activity is created the first time (i.e., avoids
        // duplicate logging during rotation)
        if (savedInstanceState == null) {
            logCommand(Action.Command.ENTRY);
        }
    }

    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        mLastTouchPos.set(motionEvent.getX(), motionEvent.getY());
        return false;
    }

    @Override
    public boolean onLongClick(View view) {
        // Find the position of the preview relative to the touch location.
        WidgetImageView img = mWidgetCell.getWidgetView();

        // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
        // we abort the drag.
        if (img.getBitmap() == null) {
            return false;
        }

        Rect bounds = img.getBitmapBounds();
        bounds.offset(img.getLeft() - (int) mLastTouchPos.x, img.getTop() - (int) mLastTouchPos.y);

        // Start home and pass the draw request params
        PinItemDragListener listener = new PinItemDragListener(mRequest, bounds,
                img.getBitmap().getWidth(), img.getWidth());

        Intent homeIntent = listener.addToIntent(
                new Intent(Intent.ACTION_MAIN)
                        .addCategory(Intent.CATEGORY_HOME)
                        .setPackage(getPackageName())
                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));

        listener.initWhenReady();
        startActivity(homeIntent,
                ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
        mFinishOnPause = true;

        // Start a system drag and drop. We use a transparent bitmap as preview for system drag
        // as the preview is handled internally by launcher.
        ClipDescription description = new ClipDescription("", new String[]{listener.getMimeType()});
        ClipData data = new ClipData(description, new ClipData.Item(""));
        view.startDragAndDrop(data, new DragShadowBuilder(view) {

            @Override
            public void onDrawShadow(Canvas canvas) { }

            @Override
            public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
                outShadowSize.set(SHADOW_SIZE, SHADOW_SIZE);
                outShadowTouchPoint.set(SHADOW_SIZE / 2, SHADOW_SIZE / 2);
            }
        }, null, View.DRAG_FLAG_GLOBAL);
        return false;
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mFinishOnPause) {
            finish();
        }
    }

    private void setupShortcut() {
        PinShortcutRequestActivityInfo shortcutInfo =
                new PinShortcutRequestActivityInfo(mRequest, this);
        WidgetItem item = new WidgetItem(shortcutInfo);
        mWidgetCell.getWidgetView().setTag(new PendingAddShortcutInfo(shortcutInfo));
        mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
        mWidgetCell.ensurePreview();
    }

    private boolean setupWidget() {
        LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
                .fromProviderInfo(this, mRequest.getAppWidgetProviderInfo(this));
        if (widgetInfo.minSpanX > mIdp.numColumns || widgetInfo.minSpanY > mIdp.numRows) {
            // Cannot add widget
            return false;
        }
        mWidgetCell.setPreview(PinItemDragListener.getPreview(mRequest));

        mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
        mAppWidgetHost = new LauncherAppWidgetHost(this);

        mPendingWidgetInfo = new PendingAddWidgetInfo(widgetInfo);
        mPendingWidgetInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
        mPendingWidgetInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
        mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, mPendingWidgetInfo);

        WidgetItem item = new WidgetItem(widgetInfo, getPackageManager(), mIdp);
        mWidgetCell.getWidgetView().setTag(mPendingWidgetInfo);
        mWidgetCell.applyFromCellItem(item, mApp.getWidgetCache());
        mWidgetCell.ensurePreview();
        return true;
    }

    /**
     * Called when the cancel button is clicked.
     */
    public void onCancelClick(View v) {
        logCommand(Action.Command.CANCEL);
        finish();
    }

    /**
     * Called when place-automatically button is clicked.
     */
    public void onPlaceAutomaticallyClick(View v) {
        if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
            InstallShortcutReceiver.queueShortcut(
                    new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
            logCommand(Action.Command.CONFIRM);
            mRequest.accept();
            finish();
            return;
        }

        mPendingBindWidgetId = mAppWidgetHost.allocateAppWidgetId();
        boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
                mPendingBindWidgetId, mRequest.getAppWidgetProviderInfo(this), mWidgetOptions);
        if (success) {
            acceptWidget(mPendingBindWidgetId);
            return;
        }

        // request bind widget
        mAppWidgetHost.startBindFlow(this, mPendingBindWidgetId,
                mRequest.getAppWidgetProviderInfo(this), REQUEST_BIND_APPWIDGET);
    }

    private void acceptWidget(int widgetId) {
        InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this);
        mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
        mRequest.accept(mWidgetOptions);
        logCommand(Action.Command.CONFIRM);
        finish();
    }

    @Override
    public void onBackPressed() {
        logCommand(Action.Command.BACK);
        super.onBackPressed();
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_BIND_APPWIDGET) {
            int widgetId = data != null
                    ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mPendingBindWidgetId)
                    : mPendingBindWidgetId;
            if (resultCode == RESULT_OK) {
                acceptWidget(widgetId);
            } else {
                // Simply wait it out.
                mAppWidgetHost.deleteAppWidgetId(widgetId);
                mPendingBindWidgetId = -1;
            }
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        mPendingBindWidgetId = savedInstanceState
                .getInt(STATE_EXTRA_WIDGET_ID, mPendingBindWidgetId);
    }

    private void logCommand(int command) {
        getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
                newCommandAction(command),
                newItemTarget(mWidgetCell.getWidgetView(), mInstantAppResolver),
                newContainerTarget(ContainerType.PINITEM)), null);
    }
}
