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

#include "modules/svg/include/SkSVGFe.h"

#include "modules/svg/include/SkSVGAttribute.h"
#include "modules/svg/include/SkSVGAttributeParser.h"
#include "modules/svg/include/SkSVGFilterContext.h"
#include "modules/svg/include/SkSVGRenderContext.h"

#include <cstddef>
#include <tuple>

sk_sp<SkImageFilter> SkSVGFe::makeImageFilter(const SkSVGRenderContext& ctx,
                                              const SkSVGFilterContext& fctx) const {
    return this->onMakeImageFilter(ctx, fctx);
}

SkRect SkSVGFe::resolveBoundaries(const SkSVGRenderContext& ctx,
                                  const SkSVGFilterContext& fctx) const {
    const auto x = fX.has_value() ? *fX : SkSVGLength(0, SkSVGLength::Unit::kPercentage);
    const auto y = fY.has_value() ? *fY : SkSVGLength(0, SkSVGLength::Unit::kPercentage);
    const auto w = fWidth.has_value() ? *fWidth : SkSVGLength(100, SkSVGLength::Unit::kPercentage);
    const auto h = fHeight.has_value() ? *fHeight : SkSVGLength(100, SkSVGLength::Unit::kPercentage);

    return ctx.resolveOBBRect(x, y, w, h, fctx.primitiveUnits());
}

static bool AnyIsStandardInput(const SkSVGFilterContext& fctx,
                               const std::vector<SkSVGFeInputType>& inputs) {
    for (const auto& in : inputs) {
        switch (in.type()) {
            case SkSVGFeInputType::Type::kFilterPrimitiveReference:
                break;
            case SkSVGFeInputType::Type::kSourceGraphic:
            case SkSVGFeInputType::Type::kSourceAlpha:
            case SkSVGFeInputType::Type::kBackgroundImage:
            case SkSVGFeInputType::Type::kBackgroundAlpha:
            case SkSVGFeInputType::Type::kFillPaint:
            case SkSVGFeInputType::Type::kStrokePaint:
                return true;
            case SkSVGFeInputType::Type::kUnspecified:
                // Unspecified means previous result (which may be SourceGraphic).
                if (fctx.previousResultIsSourceGraphic()) {
                    return true;
                }
                break;
        }
    }

    return false;
}

SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx,
                                       const SkSVGFilterContext& fctx) const {
    // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion,
    // the default filter effect subregion is equal to the union of the subregions defined
    // for all "referenced nodes" (filter effect inputs). If there are no inputs, the
    // default subregion is equal to the filter effects region
    // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion).
    const std::vector<SkSVGFeInputType> inputs = this->getInputs();
    SkRect defaultSubregion;
    if (inputs.empty() || AnyIsStandardInput(fctx, inputs)) {
        defaultSubregion = fctx.filterEffectsRegion();
    } else {
        defaultSubregion = fctx.filterPrimitiveSubregion(inputs[0]);
        for (size_t i = 1; i < inputs.size(); i++) {
            defaultSubregion.join(fctx.filterPrimitiveSubregion(inputs[i]));
        }
    }

    // Next resolve the rect specified by the x, y, width, height attributes on this filter effect.
    // If those attributes were given, they override the corresponding attribute of the default
    // filter effect subregion calculated above.
    const SkRect boundaries = this->resolveBoundaries(ctx, fctx);

    // Compute and return the fully resolved subregion.
    return SkRect::MakeXYWH(fX.has_value() ? boundaries.fLeft : defaultSubregion.fLeft,
                            fY.has_value() ? boundaries.fTop : defaultSubregion.fTop,
                            fWidth.has_value() ? boundaries.width() : defaultSubregion.width(),
                            fHeight.has_value() ? boundaries.height() : defaultSubregion.height());
}

SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx,
                                           const SkSVGFilterContext&) const {
    constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB;
    const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters;
    return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs;
}

void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }

bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
    return INHERITED::parseAndSetAttribute(name, value) ||
           this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
           this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value)) ||
           this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
           this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
           this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", name, value)) ||
           this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", name, value));
}

template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
    static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
            {"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
            {"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
            {"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
            {"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
            {"FillPaint", SkSVGFeInputType::Type::kFillPaint},
            {"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
    };

    SkSVGStringType resultId;
    SkSVGFeInputType::Type t;
    bool parsedValue = false;
    if (this->parseEnumMap(gTypeMap, &t)) {
        *type = SkSVGFeInputType(t);
        parsedValue = true;
    } else if (parse(&resultId)) {
        *type = SkSVGFeInputType(resultId);
        parsedValue = true;
    }

    return parsedValue && this->parseEOSToken();
}
