/*
 * Copyright (C) 2015 Apple Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#pragma once

#include "FloatPoint.h"
#include "FloatRect.h"
#include "FloatRoundedRect.h"
#include "Font.h"
#include "GlyphBuffer.h"
#include "GraphicsContext.h"
#include "Image.h"
#include <wtf/RefCounted.h>
#include <wtf/TypeCasts.h>

#if USE(CG)
#include "GraphicsContextPlatformPrivateCG.h"
#endif

namespace WTF {
class TextStream;
}

namespace WebCore {

struct ImagePaintingOptions;

namespace DisplayList {

enum class ItemType {
    Save,
    Restore,
    Translate,
    Rotate,
    Scale,
    ConcatenateCTM,
    SetState,
    SetLineCap,
    SetLineDash,
    SetLineJoin,
    SetMiterLimit,
    ClearShadow,
    Clip,
    ClipOut,
    ClipOutToPath,
    ClipPath,
    DrawGlyphs,
    DrawImage,
    DrawTiledImage,
    DrawTiledScaledImage,
#if USE(CG) || USE(CAIRO) || USE(ULTRALIGHT)
    DrawNativeImage,
#endif
    DrawPattern,
    DrawRect,
    DrawLine,
    DrawLinesForText,
    DrawDotsForDocumentMarker,
    DrawEllipse,
    DrawPath,
    DrawFocusRingPath,
    DrawFocusRingRects,
    FillRect,
    FillRectWithColor,
    FillRectWithGradient,
    FillCompositedRect,
    FillRoundedRect,
    FillRectWithRoundedHole,
    FillPath,
    FillEllipse,
    StrokeRect,
    StrokePath,
    StrokeEllipse,
    ClearRect,
    BeginTransparencyLayer,
    EndTransparencyLayer,
#if USE(CG)
    ApplyStrokePattern, // FIXME: should not be a recorded item.
    ApplyFillPattern, // FIXME: should not be a recorded item.
#endif
    ApplyDeviceScaleFactor,
};

class Item : public RefCounted<Item> {
public:
    Item() = delete;

    Item(ItemType type)
        : m_type(type)
    {
    }

    virtual ~Item() = default;

    ItemType type() const
    {
        return m_type;
    }

    virtual void apply(GraphicsContext&) const = 0;

    static constexpr bool isDisplayListItem = true;

    virtual bool isDrawingItem() const { return false; }
    
    // A state item is one preserved by Save/Restore.
    bool isStateItem() const
    {
        return isStateItemType(m_type);
    }

    static bool isStateItemType(ItemType itemType)
    {
        switch (itemType) {
        case ItemType:: Translate:
        case ItemType:: Rotate:
        case ItemType:: Scale:
        case ItemType:: ConcatenateCTM:
        case ItemType:: SetState:
        case ItemType:: SetLineCap:
        case ItemType:: SetLineDash:
        case ItemType:: SetLineJoin:
        case ItemType:: SetMiterLimit:
        case ItemType:: ClearShadow:
            return true;
        default:
            return false;
        }
        return false;
    }

#if !defined(NDEBUG) || !LOG_DISABLED
    WTF::CString description() const;
#endif
    static size_t sizeInBytes(const Item&);

private:
    ItemType m_type;
};

class DrawingItem : public Item {
public:
    DrawingItem(ItemType type)
        : Item(type)
    {
    }

    void setExtent(const FloatRect& r) { m_extent = r; }
    const FloatRect& extent() const { return m_extent.value(); }

    bool extentKnown() const { return static_cast<bool>(m_extent); }

    // Return bounds of this drawing operation in local coordinates.
    // Does not include effets of transform, shadow etc in the state.
    virtual Optional<FloatRect> localBounds(const GraphicsContext&) const { return WTF::nullopt; }

private:
    bool isDrawingItem() const override { return true; }

    Optional<FloatRect> m_extent; // In base coordinates, taking shadows and transforms into account.
};

class Save : public Item {
public:
    static Ref<Save> create()
    {
        return adoptRef(*new Save);
    }

    // Index in the display list of the corresponding Restore item. 0 if unmatched.
    size_t restoreIndex() const { return m_restoreIndex; }
    void setRestoreIndex(size_t index) { m_restoreIndex = index; }

private:
    Save()
        : Item(ItemType::Save)
    {
    }

    void apply(GraphicsContext&) const override;
    
    size_t m_restoreIndex { 0 };
};

class Restore : public Item {
public:
    static Ref<Restore> create()
    {
        return adoptRef(*new Restore);
    }

private:
    Restore()
        : Item(ItemType::Restore)
    {
    }

    void apply(GraphicsContext&) const override;
};

class Translate : public Item {
public:
    static Ref<Translate> create(float x, float y)
    {
        return adoptRef(*new Translate(x, y));
    }

    float x() const { return m_x; }
    float y() const { return m_y; }

private:
    Translate(float x, float y)
        : Item(ItemType::Translate)
        , m_x(x)
        , m_y(y)
    {
    }

    void apply(GraphicsContext&) const override;

    float m_x;
    float m_y;
};

class Rotate : public Item {
public:
    static Ref<Rotate> create(float angleInRadians)
    {
        return adoptRef(*new Rotate(angleInRadians));
    }

    float angle() const { return m_angle; }

private:
    Rotate(float angle)
        : Item(ItemType::Rotate)
        , m_angle(angle)
    {
    }

    void apply(GraphicsContext&) const override;

    float m_angle; // In radians.
};

class Scale : public Item {
public:
    static Ref<Scale> create(const FloatSize& size)
    {
        return adoptRef(*new Scale(size));
    }

    const FloatSize& amount() const { return m_size; }

private:
    Scale(const FloatSize& size)
        : Item(ItemType::Scale)
        , m_size(size)
    {
    }

    void apply(GraphicsContext&) const override;

    FloatSize m_size;
};

class ConcatenateCTM : public Item {
public:
    static Ref<ConcatenateCTM> create(const AffineTransform& matrix)
    {
        return adoptRef(*new ConcatenateCTM(matrix));
    }

    const AffineTransform& transform() const { return m_transform; }

private:
    ConcatenateCTM(const AffineTransform&);

    void apply(GraphicsContext&) const override;

    AffineTransform m_transform;
};

class SetState : public Item {
public:
    static Ref<SetState> create(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
    {
        return adoptRef(*new SetState(state, flags));
    }
    
    const GraphicsContextStateChange& state() const { return m_state; }

    void accumulate(const GraphicsContextState&, GraphicsContextState::StateChangeFlags);

    void accumulate(GraphicsContextState&) const;

    static void applyState(GraphicsContext&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags);

    static void dumpStateChanges(WTF::TextStream&, const GraphicsContextState&, GraphicsContextState::StateChangeFlags);
private:
    SetState(const GraphicsContextState& state, GraphicsContextState::StateChangeFlags flags)
        : Item(ItemType::SetState)
        , m_state(state, flags)
    {
    }

    void apply(GraphicsContext&) const override;

    GraphicsContextStateChange m_state;
};

class SetLineCap : public Item {
public:
    static Ref<SetLineCap> create(LineCap lineCap)
    {
        return adoptRef(*new SetLineCap(lineCap));
    }
    
    LineCap lineCap() const { return m_lineCap; }

private:
    SetLineCap(LineCap lineCap)
        : Item(ItemType::SetLineCap)
        , m_lineCap(lineCap)
    {
    }

    void apply(GraphicsContext&) const override;

    LineCap m_lineCap;
};

class SetLineDash : public Item {
public:
    static Ref<SetLineDash> create(const DashArray& dashArray, float dashOffset)
    {
        return adoptRef(*new SetLineDash(dashArray, dashOffset));
    }

    const DashArray& dashArray() const { return m_dashArray; }
    float dashOffset() const { return m_dashOffset; }

private:
    SetLineDash(const DashArray& dashArray, float dashOffset)
        : Item(ItemType::SetLineDash)
        , m_dashArray(dashArray)
        , m_dashOffset(dashOffset)
    {
    }

    void apply(GraphicsContext&) const override;

    DashArray m_dashArray;
    float m_dashOffset;
};

class SetLineJoin : public Item {
public:
    static Ref<SetLineJoin> create(LineJoin lineJoin)
    {
        return adoptRef(*new SetLineJoin(lineJoin));
    }
    
    LineJoin lineJoin() const { return m_lineJoin; }

private:
    SetLineJoin(LineJoin lineJoin)
        : Item(ItemType::SetLineJoin)
        , m_lineJoin(lineJoin)
    {
    }

    void apply(GraphicsContext&) const override;

    LineJoin m_lineJoin;
};

class SetMiterLimit : public Item {
public:
    static Ref<SetMiterLimit> create(float limit)
    {
        return adoptRef(*new SetMiterLimit(limit));
    }

    float miterLimit() const { return m_miterLimit; }

private:
    SetMiterLimit(float miterLimit)
        : Item(ItemType::SetMiterLimit)
        , m_miterLimit(miterLimit)
    {
    }

    void apply(GraphicsContext&) const override;

    float m_miterLimit;
};

class ClearShadow : public Item {
public:
    static Ref<ClearShadow> create()
    {
        return adoptRef(*new ClearShadow);
    }

private:
    ClearShadow()
        : Item(ItemType::ClearShadow)
    {
    }

    void apply(GraphicsContext&) const override;
};

// FIXME: treat as DrawingItem?
class Clip : public Item {
public:
    static Ref<Clip> create(const FloatRect& rect)
    {
        return adoptRef(*new Clip(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    Clip(const FloatRect& rect)
        : Item(ItemType::Clip)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;

    FloatRect m_rect;
};

class ClipOut : public Item {
public:
    static Ref<ClipOut> create(const FloatRect& rect)
    {
        return adoptRef(*new ClipOut(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    ClipOut(const FloatRect& rect)
        : Item(ItemType::ClipOut)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;

    FloatRect m_rect;
};

class ClipOutToPath : public Item {
public:
    static Ref<ClipOutToPath> create(const Path& path)
    {
        return adoptRef(*new ClipOutToPath(path));
    }

    const Path& path() const { return m_path; }

private:
    ClipOutToPath(const Path& path)
        : Item(ItemType::ClipOutToPath)
        , m_path(path)
    {
    }

    void apply(GraphicsContext&) const override;

    const Path m_path;
};

class ClipPath : public Item {
public:
    static Ref<ClipPath> create(const Path& path, WindRule windRule)
    {
        return adoptRef(*new ClipPath(path, windRule));
    }

    const Path& path() const { return m_path; }
    WindRule windRule() const { return m_windRule; }

private:
    ClipPath(const Path& path, WindRule windRule)
        : Item(ItemType::ClipPath)
        , m_path(path)
        , m_windRule(windRule)
    {
    }

    void apply(GraphicsContext&) const override;

    const Path m_path;
    WindRule m_windRule;
};

class DrawGlyphs : public DrawingItem {
public:
    static Ref<DrawGlyphs> create(const Font& font, const GlyphBufferGlyph* glyphs, const GlyphBufferAdvance* advances, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode smoothingMode)
    {
        return adoptRef(*new DrawGlyphs(font, glyphs, advances, count, blockLocation, localAnchor, smoothingMode));
    }

    const FloatPoint& blockLocation() const { return m_blockLocation; }
    void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; }

    const FloatSize& localAnchor() const { return m_localAnchor; }

    FloatPoint anchorPoint() const { return m_blockLocation + m_localAnchor; }

    const Vector<GlyphBufferGlyph, 128>& glyphs() const { return m_glyphs; }

private:
    DrawGlyphs(const Font&, const GlyphBufferGlyph*, const GlyphBufferAdvance*, unsigned count, const FloatPoint& blockLocation, const FloatSize& localAnchor, FontSmoothingMode);

    void computeBounds();

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    GlyphBuffer generateGlyphBuffer() const;

    Ref<Font> m_font;
    Vector<GlyphBufferGlyph, 128> m_glyphs;
    Vector<GlyphBufferAdvance, 128> m_advances;
    FloatRect m_bounds;
    FloatPoint m_blockLocation;
    FloatSize m_localAnchor;
    FontSmoothingMode m_smoothingMode;
};

class DrawImage : public DrawingItem {
public:
    static Ref<DrawImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
    {
        return adoptRef(*new DrawImage(image, destination, source, imagePaintingOptions));
    }

    const Image& image() const { return m_image.get(); }
    FloatRect source() const { return m_source; }
    FloatRect destination() const { return m_destination; }

private:
    DrawImage(Image&, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions&);

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }

    mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
    FloatRect m_destination;
    FloatRect m_source;
    ImagePaintingOptions m_imagePaintingOptions;
};

class DrawTiledImage : public DrawingItem {
public:
    static Ref<DrawTiledImage> create(Image& image, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
    {
        return adoptRef(*new DrawTiledImage(image, destination, source, tileSize, spacing, imagePaintingOptions));
    }

    const Image& image() const { return m_image.get(); }
    FloatPoint source() const { return m_source; }
    FloatRect destination() const { return m_destination; }

    FloatSize tileSize() const { return m_tileSize; }
    FloatSize spacing() const { return m_spacing; }

private:
    DrawTiledImage(Image&, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions&);

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }

    mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
    FloatRect m_destination;
    FloatPoint m_source;
    FloatSize m_tileSize;
    FloatSize m_spacing;
    ImagePaintingOptions m_imagePaintingOptions;
};

class DrawTiledScaledImage : public DrawingItem {
public:
    static Ref<DrawTiledScaledImage> create(Image& image, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
    {
        return adoptRef(*new DrawTiledScaledImage(image, destination, source, tileScaleFactor, hRule, vRule, imagePaintingOptions));
    }

    const Image& image() const { return m_image.get(); }
    FloatRect source() const { return m_source; }
    FloatRect destination() const { return m_destination; }

private:
    DrawTiledScaledImage(Image&, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions&);

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }

    mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
    FloatRect m_destination;
    FloatRect m_source;
    FloatSize m_tileScaleFactor;
    Image::TileRule m_hRule;
    Image::TileRule m_vRule;
    ImagePaintingOptions m_imagePaintingOptions;
};

#if USE(CG) || USE(CAIRO) || USE(ULTRALIGHT)
class DrawNativeImage : public DrawingItem {
public:
    static Ref<DrawNativeImage> create(const NativeImagePtr& image, const FloatSize& imageSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator op, BlendMode blendMode, ImageOrientation orientation)
    {
        return adoptRef(*new DrawNativeImage(image, imageSize, destRect, srcRect, op, blendMode, orientation));
    }

    FloatRect source() const { return m_srcRect; }
    FloatRect destination() const { return m_destination; }

private:
    DrawNativeImage(const NativeImagePtr&, const FloatSize& selfSize, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator, BlendMode, ImageOrientation);

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }

#if USE(CG)
    RetainPtr<CGImageRef> m_image;
#endif
    FloatSize m_imageSize;
    FloatRect m_destination;
    FloatRect m_srcRect;
#if USE(CG)
    CompositeOperator m_op;
    BlendMode m_blendMode;
#endif
    ImageOrientation m_orientation;
};
#endif

class DrawPattern : public DrawingItem {
public:
    static Ref<DrawPattern> create(Image& image, const FloatRect& destRect, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode)
    {
        return adoptRef(*new DrawPattern(image, destRect, tileRect, patternTransform, phase, spacing, op, blendMode));
    }

    const Image& image() const { return m_image.get(); }
    const AffineTransform& patternTransform() const { return m_patternTransform; }
    FloatRect tileRect() const { return m_tileRect; }
    FloatRect destRect() const { return m_destination; }
    FloatPoint phase() const { return m_phase; }
    FloatSize spacing() const { return m_spacing; }

private:
    DrawPattern(Image&, const FloatRect& destRect, const FloatRect& srcRect, const AffineTransform&, const FloatPoint& phase, const FloatSize& spacing, CompositeOperator, BlendMode = BlendMode::Normal);

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_destination; }

    mutable Ref<Image> m_image; // FIXME: Drawing images can cause their animations to progress. This shouldn't have to be mutable.
    AffineTransform m_patternTransform;
    FloatRect m_tileRect;
    FloatRect m_destination;
    FloatPoint m_phase;
    FloatSize m_spacing;
    CompositeOperator m_op;
    BlendMode m_blendMode;
};

// Is DrawingItem because the size of the transparency layer is implicitly the clip bounds.
class BeginTransparencyLayer : public DrawingItem {
public:
    static Ref<BeginTransparencyLayer> create(float opacity)
    {
        return adoptRef(*new BeginTransparencyLayer(opacity));
    }

    float opacity() const { return m_opacity; }

private:
    BeginTransparencyLayer(float opacity)
        : DrawingItem(ItemType::BeginTransparencyLayer)
        , m_opacity(opacity)
    {
    }

    void apply(GraphicsContext&) const override;

    float m_opacity;
};

class EndTransparencyLayer : public DrawingItem {
public:
    static Ref<EndTransparencyLayer> create()
    {
        return adoptRef(*new EndTransparencyLayer);
    }

private:
    EndTransparencyLayer()
        : DrawingItem(ItemType::EndTransparencyLayer)
    {
    }

    void apply(GraphicsContext&) const override;
};

class DrawRect : public DrawingItem {
public:
    static Ref<DrawRect> create(const FloatRect& rect, float borderThickness)
    {
        return adoptRef(*new DrawRect(rect, borderThickness));
    }

    FloatRect rect() const { return m_rect; }
    float borderThickness() const { return m_borderThickness; }

private:
    DrawRect(const FloatRect& rect, float borderThickness)
        : DrawingItem(ItemType::DrawRect)
        , m_rect(rect)
        , m_borderThickness(borderThickness)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
    float m_borderThickness;
};

class DrawLine : public DrawingItem {
public:
    static Ref<DrawLine> create(const FloatPoint& point1, const FloatPoint& point2)
    {
        return adoptRef(*new DrawLine(point1, point2));
    }

    FloatPoint point1() const { return m_point1; }
    FloatPoint point2() const { return m_point2; }

private:
    DrawLine(const FloatPoint& point1, const FloatPoint& point2)
        : DrawingItem(ItemType::DrawLine)
        , m_point1(point1)
        , m_point2(point2)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    FloatPoint m_point1;
    FloatPoint m_point2;
};

class DrawLinesForText : public DrawingItem {
public:
    static Ref<DrawLinesForText> create(const FloatPoint& blockLocation, const FloatSize& localAnchor, float thickness, const DashArray& widths, bool printing, bool doubleLines)
    {
        return adoptRef(*new DrawLinesForText(blockLocation, localAnchor, thickness, widths, printing, doubleLines));
    }

    void setBlockLocation(const FloatPoint& blockLocation) { m_blockLocation = blockLocation; }
    const FloatPoint& blockLocation() const { return m_blockLocation; }
    const FloatSize& localAnchor() const { return m_localAnchor; }
    FloatPoint point() const { return m_blockLocation + m_localAnchor; }
    float thickness() const { return m_thickness; }
    const DashArray& widths() const { return m_widths; }
    bool isPrinting() const { return m_printing; }
    bool doubleLines() const { return m_doubleLines; }

private:
    DrawLinesForText(const FloatPoint& blockLocation, const FloatSize& localAnchor, float thickness, const DashArray& widths, bool printing, bool doubleLines)
        : DrawingItem(ItemType::DrawLinesForText)
        , m_blockLocation(blockLocation)
        , m_localAnchor(localAnchor)
        , m_widths(widths)
        , m_thickness(thickness)
        , m_printing(printing)
        , m_doubleLines(doubleLines)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    FloatPoint m_blockLocation;
    FloatSize m_localAnchor;
    DashArray m_widths;
    float m_thickness;
    bool m_printing;
    bool m_doubleLines;
};

class DrawDotsForDocumentMarker : public DrawingItem {
public:
    static Ref<DrawDotsForDocumentMarker> create(const FloatRect& rect, DocumentMarkerLineStyle style)
    {
        return adoptRef(*new DrawDotsForDocumentMarker(rect, style));
    }

    FloatRect rect() const { return m_rect; }

private:
    DrawDotsForDocumentMarker(const FloatRect& rect, DocumentMarkerLineStyle style)
        : DrawingItem(ItemType::DrawDotsForDocumentMarker)
        , m_rect(rect)
        , m_style(style)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    FloatRect m_rect;
    DocumentMarkerLineStyle m_style;
};

class DrawEllipse : public DrawingItem {
public:
    static Ref<DrawEllipse> create(const FloatRect& rect)
    {
        return adoptRef(*new DrawEllipse(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    DrawEllipse(const FloatRect& rect)
        : DrawingItem(ItemType::DrawEllipse)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
};

class DrawPath : public DrawingItem {
public:
    static Ref<DrawPath> create(const Path& path)
    {
        return adoptRef(*new DrawPath(path));
    }

    const Path& path() const { return m_path; }

private:
    DrawPath(const Path& path)
        : DrawingItem(ItemType::DrawPath)
        , m_path(path)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); }

    const Path m_path;
};

class DrawFocusRingPath : public DrawingItem {
public:
    static Ref<DrawFocusRingPath> create(const Path& path, float width, float offset, const Color& color)
    {
        return adoptRef(*new DrawFocusRingPath(path, width, offset, color));
    }

    const Path& path() const { return m_path; }
    float width() const { return m_width; }
    float offset() const { return m_offset; }
    const Color& color() const { return m_color; }

private:
    DrawFocusRingPath(const Path& path, float width, float offset, const Color& color)
        : DrawingItem(ItemType::DrawFocusRingPath)
        , m_path(path)
        , m_width(width)
        , m_offset(offset)
        , m_color(color)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    const Path m_path;
    float m_width;
    float m_offset;
    Color m_color;
};

class DrawFocusRingRects : public DrawingItem {
public:
    static Ref<DrawFocusRingRects> create(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
    {
        return adoptRef(*new DrawFocusRingRects(rects, width, offset, color));
    }

    const Vector<FloatRect> rects() const { return m_rects; }
    float width() const { return m_width; }
    float offset() const { return m_offset; }
    const Color& color() const { return m_color; }

private:
    DrawFocusRingRects(const Vector<FloatRect>& rects, float width, float offset, const Color& color)
        : DrawingItem(ItemType::DrawFocusRingRects)
        , m_rects(rects)
        , m_width(width)
        , m_offset(offset)
        , m_color(color)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    Vector<FloatRect> m_rects;
    float m_width;
    float m_offset;
    Color m_color;
};

class FillRect : public DrawingItem {
public:
    static Ref<FillRect> create(const FloatRect& rect)
    {
        return adoptRef(*new FillRect(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    FillRect(const FloatRect& rect)
        : DrawingItem(ItemType::FillRect)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
};

// FIXME: Make these inherit from FillRect proper.
class FillRectWithColor : public DrawingItem {
public:
    static Ref<FillRectWithColor> create(const FloatRect& rect, const Color& color)
    {
        return adoptRef(*new FillRectWithColor(rect, color));
    }

    FloatRect rect() const { return m_rect; }
    const Color& color() const { return m_color; }

private:
    FillRectWithColor(const FloatRect& rect, const Color& color)
        : DrawingItem(ItemType::FillRectWithColor)
        , m_rect(rect)
        , m_color(color)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
    Color m_color;
};

class FillRectWithGradient : public DrawingItem {
public:
    static Ref<FillRectWithGradient> create(const FloatRect& rect, Gradient& gradient)
    {
        return adoptRef(*new FillRectWithGradient(rect, gradient));
    }

    FloatRect rect() const { return m_rect; }

private:
    FillRectWithGradient(const FloatRect& rect, Gradient& gradient)
        : DrawingItem(ItemType::FillRectWithGradient)
        , m_rect(rect)
        , m_gradient(gradient)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
    mutable Ref<Gradient> m_gradient; // FIXME: Make this not mutable
};

class FillCompositedRect : public DrawingItem {
public:
    static Ref<FillCompositedRect> create(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
    {
        return adoptRef(*new FillCompositedRect(rect, color, op, blendMode));
    }

    FloatRect rect() const { return m_rect; }
    const Color& color() const { return m_color; }
    CompositeOperator compositeOperator() const { return m_op; }
    BlendMode blendMode() const { return m_blendMode; }

private:
    FillCompositedRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
        : DrawingItem(ItemType::FillCompositedRect)
        , m_rect(rect)
        , m_color(color)
        , m_op(op)
        , m_blendMode(blendMode)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
    Color m_color;
    CompositeOperator m_op;
    BlendMode m_blendMode;
};

class FillRoundedRect : public DrawingItem {
public:
    static Ref<FillRoundedRect> create(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
    {
        return adoptRef(*new FillRoundedRect(rect, color, blendMode));
    }

    const FloatRoundedRect& roundedRect() const { return m_rect; }
    const Color& color() const { return m_color; }
    BlendMode blendMode() const { return m_blendMode; }

private:
    FillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
        : DrawingItem(ItemType::FillRoundedRect)
        , m_rect(rect)
        , m_color(color)
        , m_blendMode(blendMode)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect.rect(); }

    FloatRoundedRect m_rect;
    Color m_color;
    BlendMode m_blendMode;
};

class FillRectWithRoundedHole : public DrawingItem {
public:
    static Ref<FillRectWithRoundedHole> create(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
    {
        return adoptRef(*new FillRectWithRoundedHole(rect, roundedHoleRect, color));
    }

    const FloatRect& rect() const { return m_rect; }
    const FloatRoundedRect& roundedHoleRect() const { return m_roundedHoleRect; }
    const Color& color() const { return m_color; }

private:
    FillRectWithRoundedHole(const FloatRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
        : DrawingItem(ItemType::FillRectWithRoundedHole)
        , m_rect(rect)
        , m_roundedHoleRect(roundedHoleRect)
        , m_color(color)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
    FloatRoundedRect m_roundedHoleRect;
    Color m_color;
};

class FillPath : public DrawingItem {
public:
    static Ref<FillPath> create(const Path& path)
    {
        return adoptRef(*new FillPath(path));
    }

    const Path& path() const { return m_path; }

private:
    FillPath(const Path& path)
        : DrawingItem(ItemType::FillPath)
        , m_path(path)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_path.fastBoundingRect(); }

    const Path m_path;
};

class FillEllipse : public DrawingItem {
public:
    static Ref<FillEllipse> create(const FloatRect& rect)
    {
        return adoptRef(*new FillEllipse(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    FillEllipse(const FloatRect& rect)
        : DrawingItem(ItemType::FillEllipse)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;

    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
};

class StrokeRect : public DrawingItem {
public:
    static Ref<StrokeRect> create(const FloatRect& rect, float lineWidth)
    {
        return adoptRef(*new StrokeRect(rect, lineWidth));
    }

    FloatRect rect() const { return m_rect; }
    float lineWidth() const { return m_lineWidth; }

private:
    StrokeRect(const FloatRect& rect, float lineWidth)
        : DrawingItem(ItemType::StrokeRect)
        , m_rect(rect)
        , m_lineWidth(lineWidth)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    FloatRect m_rect;
    float m_lineWidth;
};

class StrokePath : public DrawingItem {
public:
    static Ref<StrokePath> create(const Path& path)
    {
        return adoptRef(*new StrokePath(path));
    }

    const Path& path() const { return m_path; }

private:
    StrokePath(const Path& path)
        : DrawingItem(ItemType::StrokePath)
        , m_path(path)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    const Path m_path;
    FloatPoint m_blockLocation;
};

class StrokeEllipse : public DrawingItem {
public:
    static Ref<StrokeEllipse> create(const FloatRect& rect)
    {
        return adoptRef(*new StrokeEllipse(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    StrokeEllipse(const FloatRect& rect)
        : DrawingItem(ItemType::StrokeEllipse)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override;

    FloatRect m_rect;
};

class ClearRect : public DrawingItem {
public:
    static Ref<ClearRect> create(const FloatRect& rect)
    {
        return adoptRef(*new ClearRect(rect));
    }

    FloatRect rect() const { return m_rect; }

private:
    ClearRect(const FloatRect& rect)
        : DrawingItem(ItemType::ClearRect)
        , m_rect(rect)
    {
    }

    void apply(GraphicsContext&) const override;
    Optional<FloatRect> localBounds(const GraphicsContext&) const override { return m_rect; }

    FloatRect m_rect;
};

#if USE(CG)
class ApplyStrokePattern : public Item {
public:
    static Ref<ApplyStrokePattern> create()
    {
        return adoptRef(*new ApplyStrokePattern);
    }

private:
    ApplyStrokePattern()
        : Item(ItemType::ApplyStrokePattern)
    {
    }

    void apply(GraphicsContext&) const override;
};

class ApplyFillPattern : public Item {
public:
    static Ref<ApplyFillPattern> create()
    {
        return adoptRef(*new ApplyFillPattern);
    }

private:
    ApplyFillPattern()
        : Item(ItemType::ApplyFillPattern)
    {
    }

    void apply(GraphicsContext&) const override;
};
#endif

class ApplyDeviceScaleFactor : public Item {
public:
    static Ref<ApplyDeviceScaleFactor> create(float scaleFactor)
    {
        return adoptRef(*new ApplyDeviceScaleFactor(scaleFactor));
    }

    float scaleFactor() const { return m_scaleFactor; }

private:
    ApplyDeviceScaleFactor(float scaleFactor)
        : Item(ItemType::ApplyDeviceScaleFactor)
        , m_scaleFactor(scaleFactor)
    {
    }

    void apply(GraphicsContext&) const override;

    float m_scaleFactor;
};

WTF::TextStream& operator<<(WTF::TextStream&, const Item&);

} // namespace DisplayList
} // namespace WebCore


#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(ToValueTypeName, predicate) \
SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \
    static bool isType(const WebCore::DisplayList::Item& object) { return object.predicate; } \
SPECIALIZE_TYPE_TRAITS_END()

SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_DRAWINGITEM(DrawingItem, isDrawingItem())

#define SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ToValueTypeName) \
SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::DisplayList::ToValueTypeName) \
    static bool isType(const WebCore::DisplayList::Item& item) { return item.type() == WebCore::DisplayList::ItemType::ToValueTypeName; } \
SPECIALIZE_TYPE_TRAITS_END()

SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Save)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Restore)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Translate)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Rotate)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Scale)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ConcatenateCTM)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetState)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineCap)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineDash)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetLineJoin)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(SetMiterLimit)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(Clip)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOut)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipOutToPath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClipPath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawGlyphs)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawImage)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledImage)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawTiledScaledImage)
#if USE(CG) || USE(CAIRO)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawNativeImage)
#endif
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPattern)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLine)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawLinesForText)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawDotsForDocumentMarker)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawEllipse)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawPath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingPath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(DrawFocusRingRects)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithColor)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithGradient)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillCompositedRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRoundedRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillRectWithRoundedHole)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillPath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(FillEllipse)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokePath)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(StrokeEllipse)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearRect)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(BeginTransparencyLayer)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(EndTransparencyLayer)
#if USE(CG)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyStrokePattern)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyFillPattern)
#endif
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ApplyDeviceScaleFactor)
SPECIALIZE_TYPE_TRAITS_DISPLAYLIST_ITEM(ClearShadow)

