/*
 * Copyright (C) 2020 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 android.view;

import static java.util.Objects.requireNonNull;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UiThread;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.CancellationSignal;

import java.io.PrintWriter;
import java.util.function.Consumer;

/**
 * A target collects the set of contextual information for a ScrollCaptureHandler discovered during
 * a {@link View#dispatchScrollCaptureSearch scroll capture search}.
 */
public final class ScrollCaptureTarget {
    private final View mContainingView;
    private final ScrollCaptureCallback mCallback;
    private final Rect mLocalVisibleRect;
    private final Point mPositionInWindow;
    private final int mHint;
    private Rect mScrollBounds;

    private final int[] mTmpIntArr = new int[2];

    public ScrollCaptureTarget(@NonNull View scrollTarget, @NonNull Rect localVisibleRect,
            @NonNull Point positionInWindow, @NonNull ScrollCaptureCallback callback) {
        mContainingView = requireNonNull(scrollTarget);
        mHint = mContainingView.getScrollCaptureHint();
        mCallback = requireNonNull(callback);
        mLocalVisibleRect = requireNonNull(localVisibleRect);
        mPositionInWindow = requireNonNull(positionInWindow);
    }

    /**
     * @return the hint that the {@code containing view} had during the scroll capture search
     * @see View#getScrollCaptureHint()
     */
    @View.ScrollCaptureHint
    public int getHint() {
        return mHint;
    }

    /** @return the {@link ScrollCaptureCallback} for this target */
    @NonNull
    public ScrollCaptureCallback getCallback() {
        return mCallback;
    }

    /** @return the {@code containing view} for this {@link ScrollCaptureCallback callback} */
    @NonNull
    public View getContainingView() {
        return mContainingView;
    }

    /**
     * Returns the visible bounds of the containing view.
     *
     * @return the visible bounds of the {@code containing view} in view-local coordinates
     */
    @NonNull
    public Rect getLocalVisibleRect() {
        return mLocalVisibleRect;
    }

    /** @return the position of the visible bounds of the containing view within the window */
    @NonNull
    public Point getPositionInWindow() {
        return mPositionInWindow;
    }

    /**
     * @return the {@code scroll bounds} for this {@link ScrollCaptureCallback callback}
     *
     * @see ScrollCaptureCallback#onScrollCaptureSearch(CancellationSignal, Consumer)
     */
    @Nullable
    public Rect getScrollBounds() {
        return mScrollBounds;
    }

    /**
     * Sets the scroll bounds rect to the intersection of provided rect and the current bounds of
     * the {@code containing view}.
     */
    public void setScrollBounds(@Nullable Rect scrollBounds) {
        mScrollBounds = Rect.copyOrNull(scrollBounds);
        if (mScrollBounds == null) {
            return;
        }
        if (!mScrollBounds.intersect(0, 0,
                mContainingView.getWidth(), mContainingView.getHeight())) {
            mScrollBounds.setEmpty();
        }
    }

    /**
     * Refresh the local visible bounds and its offset within the window, based on the current
     * state of the {@code containing view}.
     */
    @UiThread
    public void updatePositionInWindow() {
        mContainingView.getLocationInWindow(mTmpIntArr);
        mPositionInWindow.x = mTmpIntArr[0];
        mPositionInWindow.y = mTmpIntArr[1];
    }

    public String toString() {
        return "ScrollCaptureTarget{" + "view=" + mContainingView
                + ", callback=" + mCallback
                + ", scrollBounds=" + mScrollBounds
                + ", localVisibleRect=" + mLocalVisibleRect
                + ", positionInWindow=" + mPositionInWindow
                + "}";
    }

    void dump(@NonNull PrintWriter writer) {
        View view = getContainingView();
        writer.println("view: " + view);
        writer.println("hint: " + mHint);
        writer.println("callback: " + mCallback);
        writer.println("scrollBounds: "
                + (mScrollBounds == null ? "null" : mScrollBounds.toShortString()));
        Point inWindow = getPositionInWindow();
        writer.println("positionInWindow: "
                + ((inWindow == null) ? "null" : "[" + inWindow.x + "," + inWindow.y + "]"));
        Rect localVisible = getLocalVisibleRect();
        writer.println("localVisibleRect: "
                + (localVisible == null ? "null" : localVisible.toShortString()));
    }
}
