/*
 * 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 SkSVGAttributeParser_DEFINED
#define SkSVGAttributeParser_DEFINED

#include "include/core/SkColor.h"
#include "include/core/SkScalar.h"
#include "include/core/SkTypes.h"
#include "include/private/base/SkNoncopyable.h"
#include "modules/svg/include/SkSVGTypes.h"

#include <cstdint>
#include <cstring>
#include <optional>
#include <tuple>
#include <vector>

class SkMatrix;
class SkString;

class SkSVGAttributeParser : public SkNoncopyable {
public:
    explicit SkSVGAttributeParser(const char[]);

    bool parseInteger(SkSVGIntegerType*);
    bool parseViewBox(SkSVGViewBoxType*);
    bool parsePreserveAspectRatio(SkSVGPreserveAspectRatio*);

    // TODO: Migrate all parse*() functions to this style (and delete the old version)
    //      so they can be used by parse<T>():
    bool parse(SkSVGIntegerType* v) { return parseInteger(v); }

    template <typename T> using ParseResult = std::optional<T>;

    template <typename T> static ParseResult<T> parse(const char* value) {
        T parsedValue;
        if (SkSVGAttributeParser(value).parse(&parsedValue)) {
            return parsedValue;
        }
        return {};
    }

    template <typename T>
    static ParseResult<T> parse(const char* expectedName,
                                const char* name,
                                const char* value) {
        if (!strcmp(name, expectedName)) {
            return parse<T>(value);
        }

        return ParseResult<T>();
    }

    template <typename PropertyT>
    static ParseResult<PropertyT> parseProperty(const char* expectedName,
                                                const char* name,
                                                const char* value) {
        if (strcmp(name, expectedName) != 0) {
            return ParseResult<PropertyT>();
        }

        if (!strcmp(value, "inherit")) {
            return PropertyT(SkSVGPropertyState::kInherit);
        }

        auto pr = parse<typename PropertyT::ValueT>(value);
        if (pr.has_value()) {
            return PropertyT(*pr);
        }

        return ParseResult<PropertyT>();
    }

private:
    class RestoreCurPos {
    public:
        explicit RestoreCurPos(SkSVGAttributeParser* self)
            : fSelf(self), fCurPos(self->fCurPos) {}

        ~RestoreCurPos() {
            if (fSelf) {
                fSelf->fCurPos = this->fCurPos;
            }
        }

        void clear() { fSelf = nullptr; }
    private:
        SkSVGAttributeParser* fSelf;
        const char* fCurPos;

        RestoreCurPos(           const RestoreCurPos&) = delete;
        RestoreCurPos& operator=(const RestoreCurPos&) = delete;
    };

    // Stack-only
    void* operator new(size_t) = delete;
    void* operator new(size_t, void*) = delete;

    template <typename T>
    bool parse(T*);

    template <typename F>
    bool advanceWhile(F func);

    bool matchStringToken(const char* token, const char** newPos = nullptr) const;
    bool matchHexToken(const char** newPos) const;

    bool parseWSToken();
    bool parseEOSToken();
    bool parseSepToken();
    bool parseCommaWspToken();
    bool parseExpectedStringToken(const char*);
    bool parseScalarToken(SkScalar*);
    bool parseInt32Token(int32_t*);
    bool parseEscape(SkUnichar*);
    bool parseIdentToken(SkString*);
    bool parseLengthUnitToken(SkSVGLength::Unit*);
    bool parseNamedColorToken(SkColor*);
    bool parseHexColorToken(SkColor*);
    bool parseColorComponentScalarToken(int32_t*);
    bool parseColorComponentIntegralToken(int32_t*);
    bool parseColorComponentFractionalToken(int32_t*);
    bool parseColorComponentToken(int32_t*);
    bool parseColorToken(SkColor*);
    bool parseRGBColorToken(SkColor*);
    bool parseRGBAColorToken(SkColor*);
    bool parseSVGColor(SkSVGColor*, SkSVGColor::Vars&&);
    bool parseSVGColorType(SkSVGColorType*);
    bool parseFuncIRI(SkSVGFuncIRI*);

    // Transform helpers
    bool parseMatrixToken(SkMatrix*);
    bool parseTranslateToken(SkMatrix*);
    bool parseScaleToken(SkMatrix*);
    bool parseRotateToken(SkMatrix*);
    bool parseSkewXToken(SkMatrix*);
    bool parseSkewYToken(SkMatrix*);

    // Parses a sequence of 'WS* <prefix> WS* (<nested>)', where the nested sequence
    // is handled by the passed functor.
    template <typename Func, typename T>
    bool parseParenthesized(const char* prefix, Func, T* result);

    template <typename T>
    bool parseList(std::vector<T>*);

    template <typename T, typename TArray>
    bool parseEnumMap(const TArray& arr, T* result) {
        for (size_t i = 0; i < std::size(arr); ++i) {
            if (this->parseExpectedStringToken(std::get<0>(arr[i]))) {
                *result = std::get<1>(arr[i]);
                return true;
            }
        }
        return false;
    }

    // The current position in the input string.
    const char* fCurPos;
    const char* fEndPos;

    using INHERITED = SkNoncopyable;
};

#endif // SkSVGAttributeParser_DEFINED
