/*
 * Copyright (C) 2007 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.
 */

#ifndef ANDROID_DISPLAY_DEVICE_H
#define ANDROID_DISPLAY_DEVICE_H

#include <stdlib.h>

#include <memory>
#include <optional>
#include <string>
#include <unordered_map>

#include <android/native_window.h>
#include <binder/IBinder.h>
#include <gui/LayerState.h>
#include <hardware/hwcomposer_defs.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
#include <system/window.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
#include <ui/Region.h>
#include <ui/Transform.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>

#include "DisplayHardware/DisplayIdentification.h"
#include "RenderArea.h"

namespace android {

class Fence;
class HWComposer;
class IGraphicBufferProducer;
class Layer;
class SurfaceFlinger;

struct CompositionInfo;
struct DisplayDeviceCreationArgs;
struct DisplayInfo;

namespace compositionengine {
class Display;
class DisplaySurface;
} // namespace compositionengine

class DisplayDevice : public LightRefBase<DisplayDevice> {
public:
    constexpr static float sDefaultMinLumiance = 0.0;
    constexpr static float sDefaultMaxLumiance = 500.0;

    enum {
        NO_LAYER_STACK = 0xFFFFFFFF,
    };

    explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
    virtual ~DisplayDevice();

    std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
        return mCompositionDisplay;
    }

    bool isVirtual() const { return mIsVirtual; }
    bool isPrimary() const { return mIsPrimary; }

    // isSecure indicates whether this display can be trusted to display
    // secure surfaces.
    bool isSecure() const;

    int         getWidth() const;
    int         getHeight() const;
    int         getInstallOrientation() const { return mDisplayInstallOrientation; }

    void                    setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
    const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
    void                    setLayersNeedingFences(const Vector< sp<Layer> >& layers);
    const Vector< sp<Layer> >& getLayersNeedingFences() const;

    void                    setLayerStack(uint32_t stack);
    void                    setDisplaySize(const int newWidth, const int newHeight);
    void                    setProjection(int orientation, const Rect& viewport, const Rect& frame);

    int                     getOrientation() const { return mOrientation; }
    static uint32_t         getPrimaryDisplayOrientationTransform();
    const ui::Transform& getTransform() const;
    const Rect& getViewport() const;
    const Rect& getFrame() const;
    const Rect& getScissor() const;
    bool needsFiltering() const;
    uint32_t getLayerStack() const;

    const std::optional<DisplayId>& getId() const;
    const wp<IBinder>& getDisplayToken() const { return mDisplayToken; }
    int32_t getSequenceId() const { return mSequenceId; }

    const Region& getUndefinedRegion() const;

    int32_t getSupportedPerFrameMetadata() const;

    bool hasWideColorGamut() const;
    // Whether h/w composer has native support for specific HDR type.
    bool hasHDR10PlusSupport() const;
    bool hasHDR10Support() const;
    bool hasHLGSupport() const;
    bool hasDolbyVisionSupport() const;

    // The returned HdrCapabilities is the combination of HDR capabilities from
    // hardware composer and RenderEngine. When the DisplayDevice supports wide
    // color gamut, RenderEngine is able to simulate HDR support in Display P3
    // color space for both PQ and HLG HDR contents. The minimum and maximum
    // luminance will be set to sDefaultMinLumiance and sDefaultMaxLumiance
    // respectively if hardware composer doesn't return meaningful values.
    const HdrCapabilities& getHdrCapabilities() const;

    // Return true if intent is supported by the display.
    bool hasRenderIntent(ui::RenderIntent intent) const;

    const Rect& getBounds() const;
    const Rect& bounds() const { return getBounds(); }

    void setDisplayName(const std::string& displayName);
    const std::string& getDisplayName() const { return mDisplayName; }

    /* ------------------------------------------------------------------------
     * Display power mode management.
     */
    int getPowerMode() const;
    void setPowerMode(int mode);
    bool isPoweredOn() const;

    ui::Dataspace getCompositionDataSpace() const;

    /* ------------------------------------------------------------------------
     * Display active config management.
     */
    int getActiveConfig() const;
    void setActiveConfig(int mode);

    // release HWC resources (if any) for removable displays
    void disconnect();

    /* ------------------------------------------------------------------------
     * Debugging
     */
    uint32_t getPageFlipCount() const;
    std::string getDebugName() const;
    void dump(std::string& result) const;

private:
    /*
     *  Constants, set during initialization
     */
    const sp<SurfaceFlinger> mFlinger;
    const wp<IBinder> mDisplayToken;
    const int32_t mSequenceId;

    const int mDisplayInstallOrientation;
    const std::shared_ptr<compositionengine::Display> mCompositionDisplay;

    std::string mDisplayName;
    const bool mIsVirtual;

    /*
     * Can only accessed from the main thread, these members
     * don't need synchronization.
     */

    // list of visible layers on that display
    Vector< sp<Layer> > mVisibleLayersSortedByZ;
    // list of layers needing fences
    Vector< sp<Layer> > mLayersNeedingFences;

    /*
     * Transaction state
     */
    static uint32_t displayStateOrientationToTransformOrientation(int orientation);
    static status_t orientationToTransfrom(int orientation,
                                           int w, int h, ui::Transform* tr);

    int mOrientation;
    static uint32_t sPrimaryDisplayOrientation;

    // Current power mode
    int mPowerMode;
    // Current active config
    int mActiveConfig;

    // TODO(b/74619554): Remove special cases for primary display.
    const bool mIsPrimary;
};

struct DisplayDeviceState {
    bool isVirtual() const { return !displayId.has_value(); }

    int32_t sequenceId = sNextSequenceId++;
    std::optional<DisplayId> displayId;
    sp<IGraphicBufferProducer> surface;
    uint32_t layerStack = DisplayDevice::NO_LAYER_STACK;
    Rect viewport;
    Rect frame;
    uint8_t orientation = 0;
    uint32_t width = 0;
    uint32_t height = 0;
    std::string displayName;
    bool isSecure = false;

private:
    static std::atomic<int32_t> sNextSequenceId;
};

struct DisplayDeviceCreationArgs {
    // We use a constructor to ensure some of the values are set, without
    // assuming a default value.
    DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
                              const std::optional<DisplayId>& displayId);

    const sp<SurfaceFlinger> flinger;
    const wp<IBinder> displayToken;
    const std::optional<DisplayId> displayId;

    int32_t sequenceId{0};
    bool isVirtual{false};
    bool isSecure{false};
    sp<ANativeWindow> nativeWindow;
    sp<compositionengine::DisplaySurface> displaySurface;
    int displayInstallOrientation{DisplayState::eOrientationDefault};
    bool hasWideColorGamut{false};
    HdrCapabilities hdrCapabilities;
    int32_t supportedPerFrameMetadata{0};
    std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
    int initialPowerMode{HWC_POWER_MODE_NORMAL};
    bool isPrimary{false};
};

class DisplayRenderArea : public RenderArea {
public:
    DisplayRenderArea(const sp<const DisplayDevice> device,
                      ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
          : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
                              device->getCompositionDataSpace(), rotation) {}
    DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
                      uint32_t reqHeight, ui::Dataspace reqDataSpace,
                      ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
          : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
                       getDisplayRotation(rotation, device->getInstallOrientation())),
            mDevice(device),
            mSourceCrop(sourceCrop),
            mAllowSecureLayers(allowSecureLayers) {}

    const ui::Transform& getTransform() const override { return mDevice->getTransform(); }
    Rect getBounds() const override { return mDevice->getBounds(); }
    int getHeight() const override { return mDevice->getHeight(); }
    int getWidth() const override { return mDevice->getWidth(); }
    bool isSecure() const override { return mAllowSecureLayers && mDevice->isSecure(); }
    const sp<const DisplayDevice> getDisplayDevice() const override { return mDevice; }

    bool needsFiltering() const override {
        // check if the projection from the logical display to the physical
        // display needs filtering
        if (mDevice->needsFiltering()) {
            return true;
        }

        // check if the projection from the logical render area (i.e., the
        // physical display) to the physical render area requires filtering
        const Rect sourceCrop = getSourceCrop();
        int width = sourceCrop.width();
        int height = sourceCrop.height();
        if (getRotationFlags() & ui::Transform::ROT_90) {
            std::swap(width, height);
        }
        return width != getReqWidth() || height != getReqHeight();
    }

    Rect getSourceCrop() const override {
        // use the projected display viewport by default.
        if (mSourceCrop.isEmpty()) {
            return mDevice->getScissor();
        }

        // Recompute the device transformation for the source crop.
        ui::Transform rotation;
        ui::Transform translatePhysical;
        ui::Transform translateLogical;
        ui::Transform scale;
        const Rect& viewport = mDevice->getViewport();
        const Rect& scissor = mDevice->getScissor();
        const Rect& frame = mDevice->getFrame();

        const int orientation = mDevice->getInstallOrientation();
        // Install orientation is transparent to the callers.  Apply it now.
        uint32_t flags = 0x00;
        switch (orientation) {
            case DisplayState::eOrientation90:
                flags = ui::Transform::ROT_90;
                break;
            case DisplayState::eOrientation180:
                flags = ui::Transform::ROT_180;
                break;
            case DisplayState::eOrientation270:
                flags = ui::Transform::ROT_270;
                break;
            default:
                break;
        }
        rotation.set(flags, getWidth(), getHeight());
        translateLogical.set(-viewport.left, -viewport.top);
        translatePhysical.set(scissor.left, scissor.top);
        scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
                  frame.getHeight() / float(viewport.getHeight()));
        const ui::Transform finalTransform =
                rotation * translatePhysical * scale * translateLogical;
        return finalTransform.transform(mSourceCrop);
    }

private:
    // Install orientation is transparent to the callers.  We need to cancel
    // it out by modifying rotation flags.
    static ui::Transform::orientation_flags getDisplayRotation(
            ui::Transform::orientation_flags rotation, int orientation) {
        if (orientation == DisplayState::eOrientationDefault) {
            return rotation;
        }

        // convert hw orientation into flag presentation
        // here inverse transform needed
        uint8_t hw_rot_90 = 0x00;
        uint8_t hw_flip_hv = 0x00;
        switch (orientation) {
            case DisplayState::eOrientation90:
                hw_rot_90 = ui::Transform::ROT_90;
                hw_flip_hv = ui::Transform::ROT_180;
                break;
            case DisplayState::eOrientation180:
                hw_flip_hv = ui::Transform::ROT_180;
                break;
            case DisplayState::eOrientation270:
                hw_rot_90 = ui::Transform::ROT_90;
                break;
        }

        // transform flags operation
        // 1) flip H V if both have ROT_90 flag
        // 2) XOR these flags
        uint8_t rotation_rot_90 = rotation & ui::Transform::ROT_90;
        uint8_t rotation_flip_hv = rotation & ui::Transform::ROT_180;
        if (rotation_rot_90 & hw_rot_90) {
            rotation_flip_hv = (~rotation_flip_hv) & ui::Transform::ROT_180;
        }

        return static_cast<ui::Transform::orientation_flags>(
                (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
    }

    const sp<const DisplayDevice> mDevice;
    const Rect mSourceCrop;
    const bool mAllowSecureLayers;
};

}; // namespace android

#endif // ANDROID_DISPLAY_DEVICE_H