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

#ifndef SkSVGNode_DEFINED
#define SkSVGNode_DEFINED

#include "include/core/SkRefCnt.h"
#include "modules/svg/include/SkSVGAttribute.h"
#include "modules/svg/include/SkSVGAttributeParser.h"

class SkCanvas;
class SkMatrix;
class SkPaint;
class SkPath;
class SkSVGLengthContext;
class SkSVGRenderContext;
class SkSVGValue;

enum class SkSVGTag {
    kCircle,
    kClipPath,
    kDefs,
    kEllipse,
    kFeBlend,
    kFeColorMatrix,
    kFeComponentTransfer,
    kFeComposite,
    kFeDiffuseLighting,
    kFeDisplacementMap,
    kFeDistantLight,
    kFeFlood,
    kFeFuncA,
    kFeFuncR,
    kFeFuncG,
    kFeFuncB,
    kFeGaussianBlur,
    kFeImage,
    kFeMerge,
    kFeMergeNode,
    kFeMorphology,
    kFeOffset,
    kFePointLight,
    kFeSpecularLighting,
    kFeSpotLight,
    kFeTurbulence,
    kFilter,
    kG,
    kImage,
    kLine,
    kLinearGradient,
    kMask,
    kPath,
    kPattern,
    kPolygon,
    kPolyline,
    kRadialGradient,
    kRect,
    kStop,
    kSvg,
    kText,
    kTextLiteral,
    kTextPath,
    kTSpan,
    kUse
};

#define SVG_PRES_ATTR(attr_name, attr_type, attr_inherited)                  \
private:                                                                     \
    bool set##attr_name(SkSVGAttributeParser::ParseResult<                   \
                            SkSVGProperty<attr_type, attr_inherited>>&& pr) {\
        if (pr.isValid()) { this->set##attr_name(std::move(*pr)); }          \
        return pr.isValid();                                                 \
    }                                                                        \
                                                                             \
public:                                                                      \
    const SkSVGProperty<attr_type, attr_inherited>& get##attr_name() const { \
        return fPresentationAttributes.f##attr_name;                         \
    }                                                                        \
    void set##attr_name(const SkSVGProperty<attr_type, attr_inherited>& v) { \
        auto* dest = &fPresentationAttributes.f##attr_name;                  \
        if (!dest->isInheritable() || v.isValue()) {                         \
            /* TODO: If dest is not inheritable, handle v == "inherit" */    \
            *dest = v;                                                       \
        } else {                                                             \
            dest->set(SkSVGPropertyState::kInherit);                         \
        }                                                                    \
    }                                                                        \
    void set##attr_name(SkSVGProperty<attr_type, attr_inherited>&& v) {      \
        auto* dest = &fPresentationAttributes.f##attr_name;                  \
        if (!dest->isInheritable() || v.isValue()) {                         \
            /* TODO: If dest is not inheritable, handle v == "inherit" */    \
            *dest = std::move(v);                                            \
        } else {                                                             \
            dest->set(SkSVGPropertyState::kInherit);                         \
        }                                                                    \
    }

class SK_API SkSVGNode : public SkRefCnt {
public:
    ~SkSVGNode() override;

    SkSVGTag tag() const { return fTag; }

    virtual void appendChild(sk_sp<SkSVGNode>) = 0;

    void render(const SkSVGRenderContext&) const;
    bool asPaint(const SkSVGRenderContext&, SkPaint*) const;
    SkPath asPath(const SkSVGRenderContext&) const;
    SkRect objectBoundingBox(const SkSVGRenderContext&) const;

    void setAttribute(SkSVGAttribute, const SkSVGValue&);
    bool setAttribute(const char* attributeName, const char* attributeValue);

    // TODO: consolidate with existing setAttribute
    virtual bool parseAndSetAttribute(const char* name, const char* value);

    // inherited
    SVG_PRES_ATTR(ClipRule                 , SkSVGFillRule  , true)
    SVG_PRES_ATTR(Color                    , SkSVGColorType , true)
    SVG_PRES_ATTR(ColorInterpolation       , SkSVGColorspace, true)
    SVG_PRES_ATTR(ColorInterpolationFilters, SkSVGColorspace, true)
    SVG_PRES_ATTR(FillRule                 , SkSVGFillRule  , true)
    SVG_PRES_ATTR(Fill                     , SkSVGPaint     , true)
    SVG_PRES_ATTR(FillOpacity              , SkSVGNumberType, true)
    SVG_PRES_ATTR(FontFamily               , SkSVGFontFamily, true)
    SVG_PRES_ATTR(FontSize                 , SkSVGFontSize  , true)
    SVG_PRES_ATTR(FontStyle                , SkSVGFontStyle , true)
    SVG_PRES_ATTR(FontWeight               , SkSVGFontWeight, true)
    SVG_PRES_ATTR(Stroke                   , SkSVGPaint     , true)
    SVG_PRES_ATTR(StrokeDashArray          , SkSVGDashArray , true)
    SVG_PRES_ATTR(StrokeDashOffset         , SkSVGLength    , true)
    SVG_PRES_ATTR(StrokeLineCap            , SkSVGLineCap   , true)
    SVG_PRES_ATTR(StrokeLineJoin           , SkSVGLineJoin  , true)
    SVG_PRES_ATTR(StrokeMiterLimit         , SkSVGNumberType, true)
    SVG_PRES_ATTR(StrokeOpacity            , SkSVGNumberType, true)
    SVG_PRES_ATTR(StrokeWidth              , SkSVGLength    , true)
    SVG_PRES_ATTR(TextAnchor               , SkSVGTextAnchor, true)
    SVG_PRES_ATTR(Visibility               , SkSVGVisibility, true)

    // not inherited
    SVG_PRES_ATTR(ClipPath                 , SkSVGFuncIRI   , false)
    SVG_PRES_ATTR(Display                  , SkSVGDisplay   , false)
    SVG_PRES_ATTR(Mask                     , SkSVGFuncIRI   , false)
    SVG_PRES_ATTR(Filter                   , SkSVGFuncIRI   , false)
    SVG_PRES_ATTR(Opacity                  , SkSVGNumberType, false)
    SVG_PRES_ATTR(StopColor                , SkSVGColor     , false)
    SVG_PRES_ATTR(StopOpacity              , SkSVGNumberType, false)
    SVG_PRES_ATTR(FloodColor               , SkSVGColor     , false)
    SVG_PRES_ATTR(FloodOpacity             , SkSVGNumberType, false)
    SVG_PRES_ATTR(LightingColor            , SkSVGColor     , false)

protected:
    SkSVGNode(SkSVGTag);

    static SkMatrix ComputeViewboxMatrix(const SkRect&, const SkRect&, SkSVGPreserveAspectRatio);

    // Called before onRender(), to apply local attributes to the context.  Unlike onRender(),
    // onPrepareToRender() bubbles up the inheritance chain: overriders should always call
    // INHERITED::onPrepareToRender(), unless they intend to short-circuit rendering
    // (return false).
    // Implementations are expected to return true if rendering is to continue, or false if
    // the node/subtree rendering is disabled.
    virtual bool onPrepareToRender(SkSVGRenderContext*) const;

    virtual void onRender(const SkSVGRenderContext&) const = 0;

    virtual bool onAsPaint(const SkSVGRenderContext&, SkPaint*) const { return false; }

    virtual SkPath onAsPath(const SkSVGRenderContext&) const = 0;

    virtual void onSetAttribute(SkSVGAttribute, const SkSVGValue&) {}

    virtual bool hasChildren() const { return false; }

    virtual SkRect onObjectBoundingBox(const SkSVGRenderContext&) const {
        return SkRect::MakeEmpty();
    }

private:
    SkSVGTag                    fTag;

    // FIXME: this should be sparse
    SkSVGPresentationAttributes fPresentationAttributes;

    using INHERITED = SkRefCnt;
};

#undef SVG_PRES_ATTR // presentation attributes are only defined for the base class

#define _SVG_ATTR_SETTERS(attr_name, attr_type, attr_default, set_cp, set_mv) \
    private:                                                                  \
        bool set##attr_name(                                                  \
                const SkSVGAttributeParser::ParseResult<attr_type>& pr) {     \
            if (pr.isValid()) { this->set##attr_name(*pr); }                  \
            return pr.isValid();                                              \
        }                                                                     \
        bool set##attr_name(                                                  \
                SkSVGAttributeParser::ParseResult<attr_type>&& pr) {          \
            if (pr.isValid()) { this->set##attr_name(std::move(*pr)); }       \
            return pr.isValid();                                              \
        }                                                                     \
    public:                                                                   \
        void set##attr_name(const attr_type& a) { set_cp(a); }                \
        void set##attr_name(attr_type&& a) { set_mv(std::move(a)); }

#define SVG_ATTR(attr_name, attr_type, attr_default)                        \
    private:                                                                \
        attr_type f##attr_name = attr_default;                              \
    public:                                                                 \
        const attr_type& get##attr_name() const { return f##attr_name; }    \
    _SVG_ATTR_SETTERS(                                                      \
            attr_name, attr_type, attr_default,                             \
            [this](const attr_type& a) { this->f##attr_name = a; },         \
            [this](attr_type&& a) { this->f##attr_name = std::move(a); })

#define SVG_OPTIONAL_ATTR(attr_name, attr_type)                                   \
    private:                                                                      \
        SkTLazy<attr_type> f##attr_name;                                          \
    public:                                                                       \
        const SkTLazy<attr_type>& get##attr_name() const { return f##attr_name; } \
    _SVG_ATTR_SETTERS(                                                            \
            attr_name, attr_type, attr_default,                                   \
            [this](const attr_type& a) { this->f##attr_name.set(a); },            \
            [this](attr_type&& a) { this->f##attr_name.set(std::move(a)); })

#endif // SkSVGNode_DEFINED
