/*
 * Copyright (C) 2013 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_HWUI_DISPLAY_LIST_H
#define ANDROID_HWUI_DISPLAY_LIST_H

#include <SkCamera.h>
#include <SkMatrix.h>

#include <private/hwui/DrawGlInfo.h>

#include <utils/KeyedVector.h>
#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>

#include <cutils/compiler.h>

#include <androidfw/ResourceTypes.h>

#include "Debug.h"
#include "CanvasProperty.h"
#include "DeferredDisplayList.h"
#include "GlFunctorLifecycleListener.h"
#include "Matrix.h"
#include "RenderProperties.h"

#include <vector>

class SkBitmap;
class SkPaint;
class SkPath;
class SkRegion;

namespace android {
namespace uirenderer {

class DeferredDisplayList;
class DisplayListOp;
class DisplayListCanvas;
class OpenGLRenderer;
class Rect;
class Layer;

#if HWUI_NEW_OPS
struct RecordedOp;
struct RenderNodeOp;

typedef RecordedOp BaseOpType;
typedef RenderNodeOp NodeOpType;
#else
class DrawRenderNodeOp;

typedef DisplayListOp BaseOpType;
typedef DrawRenderNodeOp NodeOpType;
#endif

/**
 * Holds data used in the playback a tree of DisplayLists.
 */
struct PlaybackStateStruct {
protected:
    PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator)
            : mRenderer(renderer)
            , mReplayFlags(replayFlags)
            , mAllocator(allocator) {}

public:
    OpenGLRenderer& mRenderer;
    const int mReplayFlags;

    // Allocator with the lifetime of a single frame. replay uses an Allocator owned by the struct,
    // while defer shares the DeferredDisplayList's Allocator
    // TODO: move this allocator to be owned by object with clear frame lifecycle
    LinearAllocator * const mAllocator;

    SkPath* allocPathForFrame() {
        return mRenderer.allocPathForFrame();
    }
};

struct DeferStateStruct : public PlaybackStateStruct {
    DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags)
            : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)),
            mDeferredList(deferredList) {}

    DeferredDisplayList& mDeferredList;
};

struct ReplayStateStruct : public PlaybackStateStruct {
    ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags)
            : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator),
            mDirty(dirty) {}

    Rect& mDirty;
    LinearAllocator mReplayAllocator;
};

/**
 * Functor that can be used for objects with data in both UI thread and RT to keep the data
 * in sync. This functor, when added to DisplayList, will be call during DisplayList sync.
 */
struct PushStagingFunctor {
    PushStagingFunctor() {}
    virtual ~PushStagingFunctor() {}
    virtual void operator ()() {}
};

struct FunctorContainer {
    Functor* functor;
    GlFunctorLifecycleListener* listener;
};

/**
 * Data structure that holds the list of commands used in display list stream
 */
class DisplayList {
    friend class DisplayListCanvas;
    friend class RecordingCanvas;
public:
    struct Chunk {
        // range of included ops in DisplayList::ops()
        size_t beginOpIndex;
        size_t endOpIndex;

        // range of included children in DisplayList::children()
        size_t beginChildIndex;
        size_t endChildIndex;

        // whether children with non-zero Z in the chunk should be reordered
        bool reorderChildren;
#if HWUI_NEW_OPS
        const ClipBase* reorderClip;
#endif
    };

    DisplayList();
    ~DisplayList();

    // index of DisplayListOp restore, after which projected descendants should be drawn
    int projectionReceiveIndex;

    const LsaVector<Chunk>& getChunks() const { return chunks; }
    const LsaVector<BaseOpType*>& getOps() const { return ops; }

    const LsaVector<NodeOpType*>& getChildren() const { return children; }

    const LsaVector<const SkBitmap*>& getBitmapResources() const { return bitmapResources; }
    const LsaVector<FunctorContainer>& getFunctors() const { return functors; }
    const LsaVector<PushStagingFunctor*>& getPushStagingFunctors() { return pushStagingFunctors; }

    size_t addChild(NodeOpType* childOp);


    void ref(VirtualLightRefBase* prop) {
        referenceHolders.push_back(prop);
    }

    size_t getUsedSize() {
        return allocator.usedSize();
    }
    bool isEmpty() {
#if HWUI_NEW_OPS
        return ops.empty();
#else
        return !hasDrawOps;
#endif
    }

private:
    // allocator into which all ops and LsaVector arrays allocated
    LinearAllocator allocator;
    LinearStdAllocator<void*> stdAllocator;

    LsaVector<Chunk> chunks;
    LsaVector<BaseOpType*> ops;

    // list of Ops referring to RenderNode children for quick, non-drawing traversal
    LsaVector<NodeOpType*> children;

    // Resources - Skia objects + 9 patches referred to by this DisplayList
    LsaVector<const SkBitmap*> bitmapResources;
    LsaVector<const SkPath*> pathResources;
    LsaVector<const Res_png_9patch*> patchResources;
    LsaVector<std::unique_ptr<const SkPaint>> paints;
    LsaVector<std::unique_ptr<const SkRegion>> regions;
    LsaVector< sp<VirtualLightRefBase> > referenceHolders;

    // List of functors
    LsaVector<FunctorContainer> functors;

    // List of functors that need to be notified of pushStaging. Note that this list gets nothing
    // but a callback during sync DisplayList, unlike the list of functors defined above, which
    // gets special treatment exclusive for webview.
    LsaVector<PushStagingFunctor*> pushStagingFunctors;

    bool hasDrawOps; // only used if !HWUI_NEW_OPS

    void cleanupResources();
};

}; // namespace uirenderer
}; // namespace android

#endif // ANDROID_HWUI_OPENGL_RENDERER_H
