/*
 * Copyright (C) 2018 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.server.wm;

import static android.view.SurfaceControl.HIDDEN;

import android.graphics.Rect;
import android.view.SurfaceControl;

import java.util.function.Supplier;

/**
 * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an
 * outer rect and an inner rect.
 */
public class Letterbox {

    private static final Rect EMPTY_RECT = new Rect();

    private final Supplier<SurfaceControl.Builder> mFactory;
    private final Rect mOuter = new Rect();
    private final Rect mInner = new Rect();
    private final LetterboxSurface mTop = new LetterboxSurface("top");
    private final LetterboxSurface mLeft = new LetterboxSurface("left");
    private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
    private final LetterboxSurface mRight = new LetterboxSurface("right");

    /**
     * Constructs a Letterbox.
     *
     * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
     */
    public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
        mFactory = surfaceControlFactory;
    }

    /**
     * Sets the dimensions of the the letterbox, such that the area between the outer and inner
     * frames will be covered by black color surfaces.
     *
     * @param t     a transaction in which to set the dimensions
     * @param outer the outer frame of the letterbox (this frame will be black, except the area
     *              that intersects with the {code inner} frame).
     * @param inner the inner frame of the letterbox (this frame will be clear)
     */
    public void setDimensions(SurfaceControl.Transaction t, Rect outer, Rect inner) {
        mOuter.set(outer);
        mInner.set(inner);

        mTop.setRect(t, outer.left, outer.top, inner.right, inner.top);
        mLeft.setRect(t, outer.left, inner.top, inner.left, outer.bottom);
        mBottom.setRect(t, inner.left, inner.bottom, outer.right, outer.bottom);
        mRight.setRect(t, inner.right, outer.top, outer.right, inner.bottom);
    }

    /**
     * Hides the letterbox.
     *
     * @param t a transaction in which to hide the letterbox
     */
    public void hide(SurfaceControl.Transaction t) {
        setDimensions(t, EMPTY_RECT, EMPTY_RECT);
    }

    /**
     * Destroys the managed {@link SurfaceControl}s.
     */
    public void destroy() {
        mOuter.setEmpty();
        mInner.setEmpty();

        mTop.destroy();
        mLeft.destroy();
        mBottom.destroy();
        mRight.destroy();
    }

    private class LetterboxSurface {

        private final String mType;
        private SurfaceControl mSurface;

        private int mLastLeft = 0;
        private int mLastTop = 0;
        private int mLastRight = 0;
        private int mLastBottom = 0;

        public LetterboxSurface(String type) {
            mType = type;
        }

        public void setRect(SurfaceControl.Transaction t,
                int left, int top, int right, int bottom) {
            if (mLastLeft == left && mLastTop == top
                    && mLastRight == right && mLastBottom == bottom) {
                // Nothing changed.
                return;
            }

            if (left < right && top < bottom) {
                if (mSurface == null) {
                    createSurface();
                }
                t.setPosition(mSurface, left, top);
                t.setSize(mSurface, right - left, bottom - top);
                t.show(mSurface);
            } else if (mSurface != null) {
                t.hide(mSurface);
            }

            mLastLeft = left;
            mLastTop = top;
            mLastRight = right;
            mLastBottom = bottom;
        }

        private void createSurface() {
            mSurface = mFactory.get().setName("Letterbox - " + mType)
                    .setFlags(HIDDEN).setColorLayer(true).build();
            mSurface.setLayer(-1);
            mSurface.setColor(new float[]{0, 0, 0});
        }

        public void destroy() {
            if (mSurface != null) {
                mSurface.destroy();
                mSurface = null;
            }
        }
    }
}
