/*
 * Copyright (C) 2021 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.window;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.SurfaceControl;

import java.util.Arrays;
import java.util.Objects;

/**
 * Represents a set of {@link android.view.SurfaceControl.Transaction} operations used to
 * operate on the {@link android.view.SurfaceControl} for picture-in-picture.
 *
 * @hide
 */
public final class PictureInPictureSurfaceTransaction implements Parcelable {
    private static final float NOT_SET = -1f;

    public final float mAlpha;
    public final PointF mPosition;

    public final float[] mFloat9;

    // Though this can be determined by mFloat9, it's easier to set the value directly
    public final float mRotation;

    public final float mCornerRadius;

    public final float mShadowRadius;

    private final Rect mWindowCrop;

    private boolean mShouldDisableCanAffectSystemUiFlags;

    private PictureInPictureSurfaceTransaction(Parcel in) {
        mAlpha = in.readFloat();
        mPosition = in.readTypedObject(PointF.CREATOR);
        mFloat9 = new float[9];
        in.readFloatArray(mFloat9);
        mRotation = in.readFloat();
        mCornerRadius = in.readFloat();
        mShadowRadius = in.readFloat();
        mWindowCrop = in.readTypedObject(Rect.CREATOR);
        mShouldDisableCanAffectSystemUiFlags = in.readBoolean();
    }

    private PictureInPictureSurfaceTransaction(float alpha, @Nullable PointF position,
            @Nullable float[] float9, float rotation, float cornerRadius, float shadowRadius,
            @Nullable Rect windowCrop) {
        mAlpha = alpha;
        mPosition = position;
        if (float9 == null) {
            mFloat9 = new float[9];
            Matrix.IDENTITY_MATRIX.getValues(mFloat9);
            mRotation = 0;
        } else {
            mFloat9 = Arrays.copyOf(float9, 9);
            mRotation = rotation;
        }
        mCornerRadius = cornerRadius;
        mShadowRadius = shadowRadius;
        mWindowCrop = (windowCrop == null) ? null : new Rect(windowCrop);
    }

    public PictureInPictureSurfaceTransaction(PictureInPictureSurfaceTransaction other) {
        this(other.mAlpha, other.mPosition,
                other.mFloat9, other.mRotation, other.mCornerRadius, other.mShadowRadius,
                other.mWindowCrop);
        mShouldDisableCanAffectSystemUiFlags = other.mShouldDisableCanAffectSystemUiFlags;
    }

    /** @return {@link Matrix} from {@link #mFloat9} */
    public Matrix getMatrix() {
        final Matrix matrix = new Matrix();
        matrix.setValues(mFloat9);
        return matrix;
    }

    /** @return {@code true} if this transaction contains setting corner radius. */
    public boolean hasCornerRadiusSet() {
        return mCornerRadius > 0;
    }

    /** @return {@code true} if this transaction contains setting shadow radius. */
    public boolean hasShadowRadiusSet() {
        return mShadowRadius > 0;
    }

    /** Sets the internal {@link #mShouldDisableCanAffectSystemUiFlags}. */
    public void setShouldDisableCanAffectSystemUiFlags(boolean shouldDisable) {
        mShouldDisableCanAffectSystemUiFlags = shouldDisable;
    }

    /** @return {@code true} if we should disable Task#setCanAffectSystemUiFlags. */
    public boolean getShouldDisableCanAffectSystemUiFlags() {
        return mShouldDisableCanAffectSystemUiFlags;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PictureInPictureSurfaceTransaction)) return false;
        PictureInPictureSurfaceTransaction that = (PictureInPictureSurfaceTransaction) o;
        return Objects.equals(mAlpha, that.mAlpha)
                && Objects.equals(mPosition, that.mPosition)
                && Arrays.equals(mFloat9, that.mFloat9)
                && Objects.equals(mRotation, that.mRotation)
                && Objects.equals(mCornerRadius, that.mCornerRadius)
                && Objects.equals(mShadowRadius, that.mShadowRadius)
                && Objects.equals(mWindowCrop, that.mWindowCrop)
                && mShouldDisableCanAffectSystemUiFlags
                == that.mShouldDisableCanAffectSystemUiFlags;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mAlpha, mPosition, Arrays.hashCode(mFloat9),
                mRotation, mCornerRadius, mShadowRadius, mWindowCrop,
                mShouldDisableCanAffectSystemUiFlags);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        out.writeFloat(mAlpha);
        out.writeTypedObject(mPosition, 0 /* flags */);
        out.writeFloatArray(mFloat9);
        out.writeFloat(mRotation);
        out.writeFloat(mCornerRadius);
        out.writeFloat(mShadowRadius);
        out.writeTypedObject(mWindowCrop, 0 /* flags */);
        out.writeBoolean(mShouldDisableCanAffectSystemUiFlags);
    }

    @Override
    public String toString() {
        final Matrix matrix = getMatrix();
        return "PictureInPictureSurfaceTransaction("
                + " alpha=" + mAlpha
                + " position=" + mPosition
                + " matrix=" + matrix.toShortString()
                + " rotation=" + mRotation
                + " cornerRadius=" + mCornerRadius
                + " shadowRadius=" + mShadowRadius
                + " crop=" + mWindowCrop
                + " shouldDisableCanAffectSystemUiFlags" + mShouldDisableCanAffectSystemUiFlags
                + ")";
    }

    /** Applies {@link PictureInPictureSurfaceTransaction} to a given leash. */
    public static void apply(@NonNull PictureInPictureSurfaceTransaction surfaceTransaction,
            @NonNull SurfaceControl surfaceControl,
            @NonNull SurfaceControl.Transaction tx) {
        final Matrix matrix = surfaceTransaction.getMatrix();
        tx.setMatrix(surfaceControl, matrix, new float[9]);
        if (surfaceTransaction.mPosition != null) {
            tx.setPosition(surfaceControl,
                    surfaceTransaction.mPosition.x, surfaceTransaction.mPosition.y);
        }
        if (surfaceTransaction.mWindowCrop != null) {
            tx.setWindowCrop(surfaceControl, surfaceTransaction.mWindowCrop);
        }
        if (surfaceTransaction.hasCornerRadiusSet()) {
            tx.setCornerRadius(surfaceControl, surfaceTransaction.mCornerRadius);
        }
        if (surfaceTransaction.hasShadowRadiusSet()) {
            tx.setShadowRadius(surfaceControl, surfaceTransaction.mShadowRadius);
        }
        if (surfaceTransaction.mAlpha != NOT_SET) {
            tx.setAlpha(surfaceControl, surfaceTransaction.mAlpha);
        }
    }

    public static final @android.annotation.NonNull Creator<PictureInPictureSurfaceTransaction>
            CREATOR =
            new Creator<PictureInPictureSurfaceTransaction>() {
                public PictureInPictureSurfaceTransaction createFromParcel(Parcel in) {
                    return new PictureInPictureSurfaceTransaction(in);
                }
                public PictureInPictureSurfaceTransaction[] newArray(int size) {
                    return new PictureInPictureSurfaceTransaction[size];
                }
            };

    public static class Builder {
        private float mAlpha = NOT_SET;
        private PointF mPosition;
        private float[] mFloat9;
        private float mRotation;
        private float mCornerRadius = NOT_SET;
        private float mShadowRadius = NOT_SET;
        private Rect mWindowCrop;

        public Builder setAlpha(float alpha) {
            mAlpha = alpha;
            return this;
        }

        public Builder setPosition(float x, float y) {
            mPosition = new PointF(x, y);
            return this;
        }

        public Builder setTransform(@NonNull float[] float9, float rotation) {
            mFloat9 = Arrays.copyOf(float9, 9);
            mRotation = rotation;
            return this;
        }

        public Builder setCornerRadius(float cornerRadius) {
            mCornerRadius = cornerRadius;
            return this;
        }

        public Builder setShadowRadius(float shadowRadius) {
            mShadowRadius = shadowRadius;
            return this;
        }

        public Builder setWindowCrop(@NonNull Rect windowCrop) {
            mWindowCrop = new Rect(windowCrop);
            return this;
        }

        public PictureInPictureSurfaceTransaction build() {
            return new PictureInPictureSurfaceTransaction(mAlpha, mPosition,
                    mFloat9, mRotation, mCornerRadius, mShadowRadius, mWindowCrop);
        }
    }
}