/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkPictureStateTree_DEFINED
#define SkPictureStateTree_DEFINED

#include "SkTDArray.h"
#include "SkChunkAlloc.h"
#include "SkDeque.h"
#include "SkMatrix.h"
#include "SkRefCnt.h"

class SkCanvas;

/**
 * Provides an interface that, given a sequence of draws into an SkPicture with corresponding
 * offsets, allows for playback of an arbitrary subset of the draws (note that Z-order is only
 * guaranteed if the draws are explicitly sorted).
 */
class SkPictureStateTree : public SkRefCnt {
private:
    struct Node;
public:
    SK_DECLARE_INST_COUNT(SkPictureStateTree)

    /**
     * A draw call, stores offset into command buffer, a pointer to the matrix, and a pointer to
     * the node in the tree that corresponds to its clip/layer state
     */
    struct Draw {
        SkMatrix* fMatrix;
        Node* fNode;
        uint32_t fOffset;
        bool operator<(const Draw& other) const { return fOffset < other.fOffset; }
    };

    class Iterator;

    SkPictureStateTree();
    ~SkPictureStateTree();

    /**
     * Creates and returns a struct representing a draw at the given offset.
     */
    Draw* appendDraw(uint32_t offset);

    /**
     * Given a list of draws, and a canvas, returns an iterator that produces the correct sequence
     * of offsets into the command buffer to carry out those calls with correct matrix/clip state.
     * This handles saves/restores, and does all necessary matrix setup.
     */
    Iterator getIterator(const SkTDArray<void*>& draws, SkCanvas* canvas);

    void appendSave();
    void appendSaveLayer(uint32_t offset);
    void appendRestore();
    void appendTransform(const SkMatrix& trans);
    void appendClip(uint32_t offset);

    /**
     * Call this immediately after an appendRestore call that is associated
     * a save or saveLayer that was removed from the command stream
     * due to a command pattern optimization in SkPicture.
     */
    void saveCollapsed();

    /**
     * Playback helper
     */
    class Iterator {
    public:
        /** Returns the next offset into the picture stream, or kDrawComplete if complete. */
        uint32_t draw();
        static const uint32_t kDrawComplete = SK_MaxU32;
        Iterator() : fPlaybackMatrix(), fValid(false) { }
        bool isValid() const { return fValid; }
    private:
        Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
        // The draws this iterator is associated with
        const SkTDArray<void*>* fDraws;

        // canvas this is playing into (so we can insert saves/restores as necessary)
        SkCanvas* fCanvas;

        // current state node
        Node* fCurrentNode;

        // List of nodes whose state we need to apply to reach TargetNode
        SkTDArray<Node*> fNodes;

        // The matrix of the canvas we're playing back into
        const SkMatrix fPlaybackMatrix;

        // Cache of current matrix, so we can avoid redundantly setting it
        SkMatrix* fCurrentMatrix;

        // current position in the array of draws
        int fPlaybackIndex;
        // Whether or not we need to do a save next iteration
        bool fSave;

        // Whether or not this is a valid iterator (the default public constructor sets this false)
        bool fValid;

        friend class SkPictureStateTree;
    };

private:

    void appendNode(uint32_t offset);

    SkChunkAlloc fAlloc;
    // Needed by saveCollapsed() because nodes do not currently store
    // references to their children.  If they did, we could just retrieve the
    // last added child.
    Node* fLastRestoredNode;

    // The currently active state
    Draw fCurrentState;
    // A stack of states for tracking save/restores
    SkDeque fStateStack;

    // Represents a notable piece of state that requires an offset into the command buffer,
    // corresponding to a clip/saveLayer/etc call, to apply.
    struct Node {
        Node* fParent;
        uint32_t fOffset;
        uint16_t fLevel;
        uint16_t fFlags;
        SkMatrix* fMatrix;
        enum Flags {
            kSave_Flag      = 0x1,
            kSaveLayer_Flag = 0x2
        };
    };

    Node fRoot;
    SkMatrix fRootMatrix;

    typedef SkRefCnt INHERITED;
};

#endif