/*
 * 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.messaging.ui;

import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;

import com.android.messaging.R;
import com.android.messaging.util.BugleActivityUtil;
import com.android.messaging.util.ImeUtil;
import com.android.messaging.util.LogUtil;
import com.android.messaging.util.UiUtils;

import java.util.HashSet;
import java.util.Set;

/**
 * Base class for app activities that use an action bar. Responsible for logging/telemetry/other
 * needs that will be common for all activities.  We can break out the common code if/when we need
 * a version that doesn't use an actionbar.
 */
public class BugleActionBarActivity extends AppCompatActivity implements ImeUtil.ImeStateHost {
    // Tracks the list of observers opting in for IME state change.
    private final Set<ImeUtil.ImeStateObserver> mImeStateObservers = new HashSet<>();

    // Tracks the soft keyboard display state
    private boolean mImeOpen;

    // The ActionMode that represents the modal contextual action bar, using our own implementation
    // rather than the built in contextual action bar to reduce jank
    private CustomActionMode mActionMode;

    // The menu for the action bar
    private Menu mActionBarMenu;

    // Used to determine if a onDisplayHeightChanged was due to the IME opening or rotation of the
    // device
    private int mLastScreenHeight;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (UiUtils.redirectToPermissionCheckIfNeeded(this)) {
            return;
        }

        mLastScreenHeight = getResources().getDisplayMetrics().heightPixels;
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onCreate");
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onStart");
        }
    }

    @Override
    protected void onRestart() {
        super.onStop();
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onRestart");
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onResume");
        }
        BugleActivityUtil.onActivityResume(this, BugleActionBarActivity.this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onPause");
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onStop");
        }
    }

    private boolean mDestroyed;

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mDestroyed = true;
    }

    public boolean getIsDestroyed() {
        return mDestroyed;
    }

    @Override
    public void onDisplayHeightChanged(final int heightSpecification) {
        int screenHeight = getResources().getDisplayMetrics().heightPixels;

        if (screenHeight != mLastScreenHeight) {
            // Appears to be an orientation change, don't fire ime updates
            mLastScreenHeight = screenHeight;
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onDisplayHeightChanged " +
                    " screenHeight: " + screenHeight + " lastScreenHeight: " + mLastScreenHeight +
                    " Skipped, appears to be orientation change.");
            return;
        }
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null && actionBar.isShowing()) {
            screenHeight -= actionBar.getHeight();
        }
        final int height = View.MeasureSpec.getSize(heightSpecification);

        final boolean imeWasOpen = mImeOpen;
        mImeOpen = screenHeight - height > 100;

        if (LogUtil.isLoggable(LogUtil.BUGLE_TAG, LogUtil.VERBOSE)) {
            LogUtil.v(LogUtil.BUGLE_TAG, this.getLocalClassName() + ".onDisplayHeightChanged " +
                    "imeWasOpen: " + imeWasOpen + " mImeOpen: " + mImeOpen + " screenHeight: " +
                    screenHeight + " height: " + height);
        }

        if (imeWasOpen != mImeOpen) {
            for (final ImeUtil.ImeStateObserver observer : mImeStateObservers) {
                observer.onImeStateChanged(mImeOpen);
            }
        }
    }

    @Override
    public void registerImeStateObserver(final ImeUtil.ImeStateObserver observer) {
        mImeStateObservers.add(observer);
    }

    @Override
    public void unregisterImeStateObserver(final ImeUtil.ImeStateObserver observer) {
        mImeStateObservers.remove(observer);
    }

    @Override
    public boolean isImeOpen() {
        return mImeOpen;
    }

    @Override
    public boolean onCreateOptionsMenu(final Menu menu) {
        mActionBarMenu = menu;
        if (mActionMode != null &&
                mActionMode.getCallback().onCreateActionMode(mActionMode, menu)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {
        mActionBarMenu = menu;
        if (mActionMode != null &&
                mActionMode.getCallback().onPrepareActionMode(mActionMode, menu)) {
            return true;
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem menuItem) {
        if (mActionMode != null &&
                mActionMode.getCallback().onActionItemClicked(mActionMode, menuItem)) {
            return true;
        }

        switch (menuItem.getItemId()) {
            case android.R.id.home:
                if (mActionMode != null) {
                    dismissActionMode();
                    return true;
                }
        }
        return super.onOptionsItemSelected(menuItem);
    }

    @Override
    public ActionMode startActionMode(final ActionMode.Callback callback) {
        mActionMode = new CustomActionMode(callback);
        supportInvalidateOptionsMenu();
        invalidateActionBar();
        return mActionMode;
    }

    public void dismissActionMode() {
        if (mActionMode != null) {
            mActionMode.finish();
            mActionMode = null;
            invalidateActionBar();
        }
    }

    public ActionMode getActionMode() {
        return mActionMode;
    }

    protected ActionMode.Callback getActionModeCallback() {
        if (mActionMode == null) {
            return null;
        }

        return mActionMode.getCallback();
    }

    /**
     * Receives and handles action bar invalidation request from sub-components of this activity.
     *
     * <p>Normally actions have sole control over the action bar, but in order to support seamless
     * transitions for components such as the full screen media picker, we have to let it take over
     * the action bar and then restore its state afterwards</p>
     *
     * <p>If a fragment does anything that may change the action bar, it should call this method
     * and then it is this method's responsibility to figure out which component "controls" the
     * action bar and delegate the updating of the action bar to that component</p>
     */
    public final void invalidateActionBar() {
        if (mActionMode != null) {
            mActionMode.updateActionBar(getSupportActionBar());
        } else {
            updateActionBar(getSupportActionBar());
        }
    }

    protected void updateActionBar(final ActionBar actionBar) {
        actionBar.setHomeAsUpIndicator(null);
    }

    /**
     * Custom ActionMode implementation which allows us to just replace the contents of the main
     * action bar rather than overlay over it
     */
    private class CustomActionMode extends ActionMode {
        private CharSequence mTitle;
        private CharSequence mSubtitle;
        private View mCustomView;
        private final Callback mCallback;

        public CustomActionMode(final Callback callback) {
            mCallback = callback;
        }

        @Override
        public void setTitle(final CharSequence title) {
            mTitle = title;
        }

        @Override
        public void setTitle(final int resId) {
            mTitle = getResources().getString(resId);
        }

        @Override
        public void setSubtitle(final CharSequence subtitle) {
            mSubtitle = subtitle;
        }

        @Override
        public void setSubtitle(final int resId) {
            mSubtitle = getResources().getString(resId);
        }

        @Override
        public void setCustomView(final View view) {
            mCustomView = view;
        }

        @Override
        public void invalidate() {
            invalidateActionBar();
        }

        @Override
        public void finish() {
            mActionMode = null;
            mCallback.onDestroyActionMode(this);
            supportInvalidateOptionsMenu();
            invalidateActionBar();
        }

        @Override
        public Menu getMenu() {
            return mActionBarMenu;
        }

        @Override
        public CharSequence getTitle() {
            return mTitle;
        }

        @Override
        public CharSequence getSubtitle() {
            return mSubtitle;
        }

        @Override
        public View getCustomView() {
            return mCustomView;
        }

        @Override
        public MenuInflater getMenuInflater() {
            return BugleActionBarActivity.this.getMenuInflater();
        }

        public Callback getCallback() {
            return mCallback;
        }

        public void updateActionBar(final ActionBar actionBar) {
            actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP);
            actionBar.setDisplayShowTitleEnabled(false);
            actionBar.setDisplayShowCustomEnabled(false);
            mActionMode.getCallback().onPrepareActionMode(mActionMode, mActionBarMenu);
            actionBar.setBackgroundDrawable(new ColorDrawable(
                    getResources().getColor(R.color.contextual_action_bar_background_color)));
            actionBar.setHomeAsUpIndicator(R.drawable.ic_cancel_small_dark);
            actionBar.show();
        }
    }
}
