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

import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;

import androidx.annotation.NonNull;

import com.android.launcher3.Utilities;
import com.android.launcher3.icons.FastBitmapDrawable;

/**
 * A composed Drawable consisting of the two app pair icons and the background behind them (looks
 * like two rectangles).
 */
public class AppPairIconDrawable extends Drawable {
    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final AppPairIconDrawingParams mP;
    private final FastBitmapDrawable mIcon1;
    private final FastBitmapDrawable mIcon2;

    /**
     * Null values to use with
     * {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}, since there
     * doesn't seem to be any other API for drawing rectangles with 4 different corner radii.
     */
    private static final RectF EMPTY_RECT = new RectF();
    private static final float[] ARRAY_OF_ZEROES = new float[8];

    AppPairIconDrawable(
            AppPairIconDrawingParams p, FastBitmapDrawable icon1, FastBitmapDrawable icon2) {
        mP = p;
        mBackgroundPaint.setStyle(Paint.Style.FILL);
        mBackgroundPaint.setColor(p.getBgColor());
        mIcon1 = icon1;
        mIcon2 = icon2;
    }

    @Override
    public void draw(@NonNull Canvas canvas) {
        if (mP.isLeftRightSplit()) {
            drawLeftRightSplit(canvas);
        } else {
            drawTopBottomSplit(canvas);
        }

        canvas.translate(
                mP.getStandardIconPadding() + mP.getOuterPadding(),
                mP.getStandardIconPadding() + mP.getOuterPadding()
        );

        // Draw first icon.
        canvas.save();
        // The app icons are placed differently depending on device orientation.
        if (mP.isLeftRightSplit()) {
            canvas.translate(
                    mP.getInnerPadding(),
                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
            );
        } else {
            canvas.translate(
                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
                    mP.getInnerPadding()
            );
        }

        mIcon1.draw(canvas);
        canvas.restore();

        // Draw second icon.
        canvas.save();
        // The app icons are placed differently depending on device orientation.
        if (mP.isLeftRightSplit()) {
            canvas.translate(
                    mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize()),
                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f
            );
        } else {
            canvas.translate(
                    mP.getBackgroundSize() / 2f - mP.getMemberIconSize() / 2f,
                    mP.getBackgroundSize() - (mP.getInnerPadding() + mP.getMemberIconSize())
            );
        }

        mIcon2.draw(canvas);
        canvas.restore();
    }

    /**
     * When device is in landscape, we draw the rectangles with a left-right split.
     */
    private void drawLeftRightSplit(Canvas canvas) {
        // Get the bounds where we will draw the background image
        int width = mP.getIconSize();
        int height = mP.getIconSize();

        // The left half of the background image, excluding center channel
        RectF leftSide = new RectF(
                mP.getStandardIconPadding() + mP.getOuterPadding(),
                mP.getStandardIconPadding() + mP.getOuterPadding(),
                (width / 2f) - (mP.getCenterChannelSize() / 2f),
                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
        );
        // The right half of the background image, excluding center channel
        RectF rightSide = new RectF(
                (width / 2f) + (mP.getCenterChannelSize() / 2f),
                (mP.getStandardIconPadding() + mP.getOuterPadding()),
                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
        );

        // Scale each background from its center edge closest to the center channel.
        Utilities.scaleRectFAboutPivot(
                leftSide,
                leftSide.left + leftSide.width(),
                leftSide.top + leftSide.centerY(),
                mP.getHoverScale());
        Utilities.scaleRectFAboutPivot(
                rightSide,
                rightSide.left,
                rightSide.top + rightSide.centerY(),
                mP.getHoverScale());

        drawCustomRoundedRect(canvas, leftSide, new float[]{
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getBigRadius(), mP.getBigRadius()});
        drawCustomRoundedRect(canvas, rightSide, new float[]{
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getSmallRadius(), mP.getSmallRadius()});
    }

    /**
     * When device is in portrait, we draw the rectangles with a top-bottom split.
     */
    private void drawTopBottomSplit(Canvas canvas) {
        // Get the bounds where we will draw the background image
        int width = mP.getIconSize();
        int height = mP.getIconSize();

        // The top half of the background image, excluding center channel
        RectF topSide = new RectF(
                (mP.getStandardIconPadding() + mP.getOuterPadding()),
                (mP.getStandardIconPadding() + mP.getOuterPadding()),
                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
                (height / 2f) - (mP.getCenterChannelSize() / 2f)
        );
        // The bottom half of the background image, excluding center channel
        RectF bottomSide = new RectF(
                (mP.getStandardIconPadding() + mP.getOuterPadding()),
                (height / 2f) + (mP.getCenterChannelSize() / 2f),
                width - (mP.getStandardIconPadding() + mP.getOuterPadding()),
                height - (mP.getStandardIconPadding() + mP.getOuterPadding())
        );

        // Scale each background from its center edge closest to the center channel.
        Utilities.scaleRectFAboutPivot(
                topSide,
                topSide.left + topSide.centerX(),
                topSide.top + topSide.height(),
                mP.getHoverScale());
        Utilities.scaleRectFAboutPivot(
                bottomSide,
                bottomSide.left + bottomSide.centerX(),
                bottomSide.top,
                mP.getHoverScale());

        drawCustomRoundedRect(canvas, topSide, new float[]{
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getSmallRadius(), mP.getSmallRadius()});
        drawCustomRoundedRect(canvas, bottomSide, new float[]{
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getSmallRadius(), mP.getSmallRadius(),
                mP.getBigRadius(), mP.getBigRadius(),
                mP.getBigRadius(), mP.getBigRadius()});
    }

    /**
     * Draws a rectangle with custom rounded corners.
     * @param c The Canvas to draw on.
     * @param rect The bounds of the rectangle.
     * @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
     *              right y, bottom right x, and so on.
     */
    private void drawCustomRoundedRect(Canvas c, RectF rect, float[] radii) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // Canvas.drawDoubleRoundRect is supported from Q onward
            c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, mBackgroundPaint);
        } else {
            // Fallback rectangle with uniform rounded corners
            c.drawRoundRect(rect, mP.getBigRadius(), mP.getBigRadius(), mBackgroundPaint);
        }
    }

    @Override
    public int getOpacity() {
        return PixelFormat.OPAQUE;
    }

    @Override
    public void setAlpha(int i) {
        mBackgroundPaint.setAlpha(i);
    }

    @Override
    public void setColorFilter(ColorFilter colorFilter) {
        mBackgroundPaint.setColorFilter(colorFilter);
    }

    @Override
    public int getIntrinsicWidth() {
        return mP.getIconSize();
    }

    @Override
    public int getIntrinsicHeight() {
        return mP.getIconSize();
    }
}
