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

#include "experimental/svg/model/SkSVGAttributeParser.h"
#include "experimental/svg/model/SkSVGCircle.h"
#include "experimental/svg/model/SkSVGClipPath.h"
#include "experimental/svg/model/SkSVGDOM.h"
#include "experimental/svg/model/SkSVGDefs.h"
#include "experimental/svg/model/SkSVGEllipse.h"
#include "experimental/svg/model/SkSVGG.h"
#include "experimental/svg/model/SkSVGLine.h"
#include "experimental/svg/model/SkSVGLinearGradient.h"
#include "experimental/svg/model/SkSVGNode.h"
#include "experimental/svg/model/SkSVGPath.h"
#include "experimental/svg/model/SkSVGPattern.h"
#include "experimental/svg/model/SkSVGPoly.h"
#include "experimental/svg/model/SkSVGRadialGradient.h"
#include "experimental/svg/model/SkSVGRect.h"
#include "experimental/svg/model/SkSVGRenderContext.h"
#include "experimental/svg/model/SkSVGSVG.h"
#include "experimental/svg/model/SkSVGStop.h"
#include "experimental/svg/model/SkSVGText.h"
#include "experimental/svg/model/SkSVGTypes.h"
#include "experimental/svg/model/SkSVGUse.h"
#include "experimental/svg/model/SkSVGValue.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkString.h"
#include "include/private/SkTo.h"
#include "include/utils/SkParsePath.h"
#include "src/core/SkTSearch.h"
#include "src/xml/SkDOM.h"

namespace {

bool SetPaintAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                       const char* stringValue) {
    SkSVGPaint paint;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parsePaint(&paint)) {
        return false;
    }

    node->setAttribute(attr, SkSVGPaintValue(paint));
    return true;
}

bool SetColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                       const char* stringValue) {
    SkSVGColorType color;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseColor(&color)) {
        return false;
    }

    node->setAttribute(attr, SkSVGColorValue(color));
    return true;
}

bool SetIRIAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                      const char* stringValue) {
    SkSVGStringType iri;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseIRI(&iri)) {
        return false;
    }

    node->setAttribute(attr, SkSVGStringValue(iri));
    return true;
}

bool SetClipPathAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                          const char* stringValue) {
    SkSVGClip clip;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseClipPath(&clip)) {
        return false;
    }

    node->setAttribute(attr, SkSVGClipValue(clip));
    return true;
}


bool SetPathDataAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                          const char* stringValue) {
    SkPath path;
    if (!SkParsePath::FromSVGString(stringValue, &path)) {
        return false;
    }

    node->setAttribute(attr, SkSVGPathValue(path));
    return true;
}

bool SetStringAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                           const char* stringValue) {
    SkString str(stringValue, strlen(stringValue));
    SkSVGStringType strType = SkSVGStringType(str);
    node->setAttribute(attr, SkSVGStringValue(strType));
    return true;
}

bool SetTransformAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                           const char* stringValue) {
    SkSVGTransformType transform;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseTransform(&transform)) {
        return false;
    }

    node->setAttribute(attr, SkSVGTransformValue(transform));
    return true;
}

bool SetLengthAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                        const char* stringValue) {
    SkSVGLength length;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseLength(&length)) {
        return false;
    }

    node->setAttribute(attr, SkSVGLengthValue(length));
    return true;
}

bool SetNumberAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                        const char* stringValue) {
    SkSVGNumberType number;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseNumber(&number)) {
        return false;
    }

    node->setAttribute(attr, SkSVGNumberValue(number));
    return true;
}

bool SetViewBoxAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                         const char* stringValue) {
    SkSVGViewBoxType viewBox;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseViewBox(&viewBox)) {
        return false;
    }

    node->setAttribute(attr, SkSVGViewBoxValue(viewBox));
    return true;
}

bool SetLineCapAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                         const char* stringValue) {
    SkSVGLineCap lineCap;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseLineCap(&lineCap)) {
        return false;
    }

    node->setAttribute(attr, SkSVGLineCapValue(lineCap));
    return true;
}

bool SetLineJoinAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                          const char* stringValue) {
    SkSVGLineJoin lineJoin;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseLineJoin(&lineJoin)) {
        return false;
    }

    node->setAttribute(attr, SkSVGLineJoinValue(lineJoin));
    return true;
}

bool SetSpreadMethodAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                             const char* stringValue) {
    SkSVGSpreadMethod spread;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseSpreadMethod(&spread)) {
        return false;
    }

    node->setAttribute(attr, SkSVGSpreadMethodValue(spread));
    return true;
}

bool SetStopColorAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                           const char* stringValue) {
    SkSVGStopColor stopColor;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseStopColor(&stopColor)) {
        return false;
    }

    node->setAttribute(attr, SkSVGStopColorValue(stopColor));
    return true;
}

bool SetPointsAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                        const char* stringValue) {
    SkSVGPointsType points;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parsePoints(&points)) {
        return false;
    }

    node->setAttribute(attr, SkSVGPointsValue(points));
    return true;
}

bool SetFillRuleAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                          const char* stringValue) {
    SkSVGFillRule fillRule;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseFillRule(&fillRule)) {
        return false;
    }

    node->setAttribute(attr, SkSVGFillRuleValue(fillRule));
    return true;
}

bool SetVisibilityAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                            const char* stringValue) {
    SkSVGVisibility visibility;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseVisibility(&visibility)) {
        return false;
    }

    node->setAttribute(attr, SkSVGVisibilityValue(visibility));
    return true;
}

bool SetDashArrayAttribute(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr,
                           const char* stringValue) {
    SkSVGDashArray dashArray;
    SkSVGAttributeParser parser(stringValue);
    if (!parser.parseDashArray(&dashArray)) {
        return false;
    }

    node->setAttribute(attr, SkSVGDashArrayValue(dashArray));
    return true;
}

SkString TrimmedString(const char* first, const char* last) {
    SkASSERT(first);
    SkASSERT(last);
    SkASSERT(first <= last);

    while (first <= last && *first <= ' ') { first++; }
    while (first <= last && *last  <= ' ') { last--; }

    SkASSERT(last - first + 1 >= 0);
    return SkString(first, SkTo<size_t>(last - first + 1));
}

// Breaks a "foo: bar; baz: ..." string into key:value pairs.
class StyleIterator {
public:
    StyleIterator(const char* str) : fPos(str) { }

    std::tuple<SkString, SkString> next() {
        SkString name, value;

        if (fPos) {
            const char* sep = this->nextSeparator();
            SkASSERT(*sep == ';' || *sep == '\0');

            const char* valueSep = strchr(fPos, ':');
            if (valueSep && valueSep < sep) {
                name  = TrimmedString(fPos, valueSep - 1);
                value = TrimmedString(valueSep + 1, sep - 1);
            }

            fPos = *sep ? sep + 1 : nullptr;
        }

        return std::make_tuple(name, value);
    }

private:
    const char* nextSeparator() const {
        const char* sep = fPos;
        while (*sep != ';' && *sep != '\0') {
            sep++;
        }
        return sep;
    }

    const char* fPos;
};

bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value);

bool SetStyleAttributes(const sk_sp<SkSVGNode>& node, SkSVGAttribute,
                        const char* stringValue) {

    SkString name, value;
    StyleIterator iter(stringValue);
    for (;;) {
        std::tie(name, value) = iter.next();
        if (name.isEmpty()) {
            break;
        }
        set_string_attribute(node, name.c_str(), value.c_str());
    }

    return true;
}

template<typename T>
struct SortedDictionaryEntry {
    const char* fKey;
    const T     fValue;
};

struct AttrParseInfo {
    SkSVGAttribute fAttr;
    bool (*fSetter)(const sk_sp<SkSVGNode>& node, SkSVGAttribute attr, const char* stringValue);
};

SortedDictionaryEntry<AttrParseInfo> gAttributeParseInfo[] = {
    { "clip-path"        , { SkSVGAttribute::kClipPath         , SetClipPathAttribute     }},
    { "clip-rule"        , { SkSVGAttribute::kClipRule         , SetFillRuleAttribute     }},
    { "color"            , { SkSVGAttribute::kColor            , SetColorAttribute        }},
    { "cx"               , { SkSVGAttribute::kCx               , SetLengthAttribute       }},
    { "cy"               , { SkSVGAttribute::kCy               , SetLengthAttribute       }},
    { "d"                , { SkSVGAttribute::kD                , SetPathDataAttribute     }},
    { "fill"             , { SkSVGAttribute::kFill             , SetPaintAttribute        }},
    { "fill-opacity"     , { SkSVGAttribute::kFillOpacity      , SetNumberAttribute       }},
    { "fill-rule"        , { SkSVGAttribute::kFillRule         , SetFillRuleAttribute     }},
    { "font-family"      , { SkSVGAttribute::kFontFamily       , SetStringAttribute       }},
    { "font-size"        , { SkSVGAttribute::kFontSize         , SetLengthAttribute       }},
    { "font-style"       , { SkSVGAttribute::kFontStyle        , SetStringAttribute       }},
    { "font-weight"      , { SkSVGAttribute::kFontWeight       , SetStringAttribute       }},
    // focal point x & y
    { "fx"               , { SkSVGAttribute::kFx               , SetLengthAttribute       }},
    { "fy"               , { SkSVGAttribute::kFy               , SetLengthAttribute       }},
    { "gradientTransform", { SkSVGAttribute::kGradientTransform, SetTransformAttribute    }},
    { "height"           , { SkSVGAttribute::kHeight           , SetLengthAttribute       }},
    { "offset"           , { SkSVGAttribute::kOffset           , SetLengthAttribute       }},
    { "opacity"          , { SkSVGAttribute::kOpacity          , SetNumberAttribute       }},
    { "patternTransform" , { SkSVGAttribute::kPatternTransform , SetTransformAttribute    }},
    { "points"           , { SkSVGAttribute::kPoints           , SetPointsAttribute       }},
    { "r"                , { SkSVGAttribute::kR                , SetLengthAttribute       }},
    { "rx"               , { SkSVGAttribute::kRx               , SetLengthAttribute       }},
    { "ry"               , { SkSVGAttribute::kRy               , SetLengthAttribute       }},
    { "spreadMethod"     , { SkSVGAttribute::kSpreadMethod     , SetSpreadMethodAttribute }},
    { "stop-color"       , { SkSVGAttribute::kStopColor        , SetStopColorAttribute    }},
    { "stop-opacity"     , { SkSVGAttribute::kStopOpacity      , SetNumberAttribute       }},
    { "stroke"           , { SkSVGAttribute::kStroke           , SetPaintAttribute        }},
    { "stroke-dasharray" , { SkSVGAttribute::kStrokeDashArray  , SetDashArrayAttribute    }},
    { "stroke-dashoffset", { SkSVGAttribute::kStrokeDashOffset , SetLengthAttribute       }},
    { "stroke-linecap"   , { SkSVGAttribute::kStrokeLineCap    , SetLineCapAttribute      }},
    { "stroke-linejoin"  , { SkSVGAttribute::kStrokeLineJoin   , SetLineJoinAttribute     }},
    { "stroke-miterlimit", { SkSVGAttribute::kStrokeMiterLimit , SetNumberAttribute       }},
    { "stroke-opacity"   , { SkSVGAttribute::kStrokeOpacity    , SetNumberAttribute       }},
    { "stroke-width"     , { SkSVGAttribute::kStrokeWidth      , SetLengthAttribute       }},
    { "style"            , { SkSVGAttribute::kUnknown          , SetStyleAttributes       }},
    { "text"             , { SkSVGAttribute::kText             , SetStringAttribute       }},
    { "text-anchor"      , { SkSVGAttribute::kTextAnchor       , SetStringAttribute       }},
    { "transform"        , { SkSVGAttribute::kTransform        , SetTransformAttribute    }},
    { "viewBox"          , { SkSVGAttribute::kViewBox          , SetViewBoxAttribute      }},
    { "visibility"       , { SkSVGAttribute::kVisibility       , SetVisibilityAttribute   }},
    { "width"            , { SkSVGAttribute::kWidth            , SetLengthAttribute       }},
    { "x"                , { SkSVGAttribute::kX                , SetLengthAttribute       }},
    { "x1"               , { SkSVGAttribute::kX1               , SetLengthAttribute       }},
    { "x2"               , { SkSVGAttribute::kX2               , SetLengthAttribute       }},
    { "xlink:href"       , { SkSVGAttribute::kHref             , SetIRIAttribute          }},
    { "y"                , { SkSVGAttribute::kY                , SetLengthAttribute       }},
    { "y1"               , { SkSVGAttribute::kY1               , SetLengthAttribute       }},
    { "y2"               , { SkSVGAttribute::kY2               , SetLengthAttribute       }},
};

SortedDictionaryEntry<sk_sp<SkSVGNode>(*)()> gTagFactories[] = {
    { "a"             , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();              }},
    { "circle"        , []() -> sk_sp<SkSVGNode> { return SkSVGCircle::Make();         }},
    { "clipPath"      , []() -> sk_sp<SkSVGNode> { return SkSVGClipPath::Make();       }},
    { "defs"          , []() -> sk_sp<SkSVGNode> { return SkSVGDefs::Make();           }},
    { "ellipse"       , []() -> sk_sp<SkSVGNode> { return SkSVGEllipse::Make();        }},
    { "g"             , []() -> sk_sp<SkSVGNode> { return SkSVGG::Make();              }},
    { "line"          , []() -> sk_sp<SkSVGNode> { return SkSVGLine::Make();           }},
    { "linearGradient", []() -> sk_sp<SkSVGNode> { return SkSVGLinearGradient::Make(); }},
    { "path"          , []() -> sk_sp<SkSVGNode> { return SkSVGPath::Make();           }},
    { "pattern"       , []() -> sk_sp<SkSVGNode> { return SkSVGPattern::Make();        }},
    { "polygon"       , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolygon();    }},
    { "polyline"      , []() -> sk_sp<SkSVGNode> { return SkSVGPoly::MakePolyline();   }},
    { "radialGradient", []() -> sk_sp<SkSVGNode> { return SkSVGRadialGradient::Make(); }},
    { "rect"          , []() -> sk_sp<SkSVGNode> { return SkSVGRect::Make();           }},
    { "stop"          , []() -> sk_sp<SkSVGNode> { return SkSVGStop::Make();           }},
    { "svg"           , []() -> sk_sp<SkSVGNode> { return SkSVGSVG::Make();            }},
    { "text"          , []() -> sk_sp<SkSVGNode> { return SkSVGText::Make();           }},
    { "use"           , []() -> sk_sp<SkSVGNode> { return SkSVGUse::Make();            }},
};

struct ConstructionContext {
    ConstructionContext(SkSVGIDMapper* mapper) : fParent(nullptr), fIDMapper(mapper) {}
    ConstructionContext(const ConstructionContext& other, const sk_sp<SkSVGNode>& newParent)
        : fParent(newParent.get()), fIDMapper(other.fIDMapper) {}

    const SkSVGNode* fParent;
    SkSVGIDMapper*   fIDMapper;
};

bool set_string_attribute(const sk_sp<SkSVGNode>& node, const char* name, const char* value) {
    const int attrIndex = SkStrSearch(&gAttributeParseInfo[0].fKey,
                                      SkTo<int>(SK_ARRAY_COUNT(gAttributeParseInfo)),
                                      name, sizeof(gAttributeParseInfo[0]));
    if (attrIndex < 0) {
#if defined(SK_VERBOSE_SVG_PARSING)
        SkDebugf("unhandled attribute: %s\n", name);
#endif
        return false;
    }

    SkASSERT(SkTo<size_t>(attrIndex) < SK_ARRAY_COUNT(gAttributeParseInfo));
    const auto& attrInfo = gAttributeParseInfo[attrIndex].fValue;
    if (!attrInfo.fSetter(node, attrInfo.fAttr, value)) {
#if defined(SK_VERBOSE_SVG_PARSING)
        SkDebugf("could not parse attribute: '%s=\"%s\"'\n", name, value);
#endif
        return false;
    }

    return true;
}

void parse_node_attributes(const SkDOM& xmlDom, const SkDOM::Node* xmlNode,
                           const sk_sp<SkSVGNode>& svgNode, SkSVGIDMapper* mapper) {
    const char* name, *value;
    SkDOM::AttrIter attrIter(xmlDom, xmlNode);
    while ((name = attrIter.next(&value))) {
        // We're handling id attributes out of band for now.
        if (!strcmp(name, "id")) {
            mapper->set(SkString(value), svgNode);
            continue;
        }
        set_string_attribute(svgNode, name, value);
    }
}

sk_sp<SkSVGNode> construct_svg_node(const SkDOM& dom, const ConstructionContext& ctx,
                                    const SkDOM::Node* xmlNode) {
    const char* elem = dom.getName(xmlNode);
    const SkDOM::Type elemType = dom.getType(xmlNode);

    if (elemType == SkDOM::kText_Type) {
        SkASSERT(dom.countChildren(xmlNode) == 0);
        // TODO: text handling
        return nullptr;
    }

    SkASSERT(elemType == SkDOM::kElement_Type);

    const int tagIndex = SkStrSearch(&gTagFactories[0].fKey,
                                     SkTo<int>(SK_ARRAY_COUNT(gTagFactories)),
                                     elem, sizeof(gTagFactories[0]));
    if (tagIndex < 0) {
#if defined(SK_VERBOSE_SVG_PARSING)
        SkDebugf("unhandled element: <%s>\n", elem);
#endif
        return nullptr;
    }

    SkASSERT(SkTo<size_t>(tagIndex) < SK_ARRAY_COUNT(gTagFactories));
    sk_sp<SkSVGNode> node = gTagFactories[tagIndex].fValue();
    parse_node_attributes(dom, xmlNode, node, ctx.fIDMapper);

    ConstructionContext localCtx(ctx, node);
    for (auto* child = dom.getFirstChild(xmlNode, nullptr); child;
         child = dom.getNextSibling(child)) {
        sk_sp<SkSVGNode> childNode = construct_svg_node(dom, localCtx, child);
        if (childNode) {
            node->appendChild(std::move(childNode));
        }
    }

    return node;
}

} // anonymous namespace

SkSVGDOM::SkSVGDOM()
    : fContainerSize(SkSize::Make(0, 0)) {
}

sk_sp<SkSVGDOM> SkSVGDOM::MakeFromDOM(const SkDOM& xmlDom) {
    sk_sp<SkSVGDOM> dom = sk_make_sp<SkSVGDOM>();

    ConstructionContext ctx(&dom->fIDMapper);
    dom->fRoot = construct_svg_node(xmlDom, ctx, xmlDom.getRootNode());

    // Reset the default container size to match the intrinsic SVG size.
    dom->setContainerSize(dom->intrinsicSize());

    return dom;
}

sk_sp<SkSVGDOM> SkSVGDOM::MakeFromStream(SkStream& svgStream) {
    SkDOM xmlDom;
    if (!xmlDom.build(svgStream)) {
        return nullptr;
    }

    return MakeFromDOM(xmlDom);
}

void SkSVGDOM::render(SkCanvas* canvas) const {
    if (fRoot) {
        SkSVGLengthContext       lctx(fContainerSize);
        SkSVGPresentationContext pctx;
        fRoot->render(SkSVGRenderContext(canvas, fIDMapper, lctx, pctx));
    }
}

SkSize SkSVGDOM::intrinsicSize() const {
    if (!fRoot || fRoot->tag() != SkSVGTag::kSvg) {
        return SkSize::Make(0, 0);
    }

    // Intrinsic sizes are never relative, so the viewport size is irrelevant.
    const SkSVGLengthContext lctx(SkSize::Make(0, 0));
    return static_cast<const SkSVGSVG*>(fRoot.get())->intrinsicSize(lctx);
}

const SkSize& SkSVGDOM::containerSize() const {
    return fContainerSize;
}

void SkSVGDOM::setContainerSize(const SkSize& containerSize) {
    // TODO: inval
    fContainerSize = containerSize;
}

sk_sp<SkSVGNode>* SkSVGDOM::findNodeById(const char* id) {
    SkString idStr(id);
    return this->fIDMapper.find(idStr);
}

void SkSVGDOM::setRoot(sk_sp<SkSVGNode> root) {
    fRoot = std::move(root);
}

// TODO(fuego): move this to SkSVGNode or its own CU.
bool SkSVGNode::setAttribute(const char* attributeName, const char* attributeValue) {
    return set_string_attribute(sk_ref_sp(this), attributeName, attributeValue);
}
