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

#include "src/gpu/GrAppliedClip.h"
#include "src/gpu/GrProcessorSet.h"
#include "src/gpu/GrUserStencilSettings.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/geometry/GrRect.h"
#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"

GrSimpleMeshDrawOpHelper::GrSimpleMeshDrawOpHelper(const MakeArgs& args, GrAAType aaType,
                                                   InputFlags inputFlags)
        : fProcessors(args.fProcessorSet)
        , fPipelineFlags((GrPipeline::InputFlags)inputFlags)
        , fAAType((int)aaType)
        , fUsesLocalCoords(false)
        , fCompatibleWithCoverageAsAlpha(false) {
    SkDEBUGCODE(fDidAnalysis = false);
    SkDEBUGCODE(fMadePipeline = false);
    if (GrAATypeIsHW(aaType)) {
        fPipelineFlags |= GrPipeline::InputFlags::kHWAntialias;
    }
}

GrSimpleMeshDrawOpHelper::~GrSimpleMeshDrawOpHelper() {
    if (fProcessors) {
        fProcessors->~GrProcessorSet();
    }
}

GrDrawOp::FixedFunctionFlags GrSimpleMeshDrawOpHelper::fixedFunctionFlags() const {
    return GrAATypeIsHW((this->aaType())) ? GrDrawOp::FixedFunctionFlags::kUsesHWAA
                                          : GrDrawOp::FixedFunctionFlags::kNone;
}

static bool none_as_coverage_aa_compatible(GrAAType aa1, GrAAType aa2) {
    return (aa1 == GrAAType::kNone && aa2 == GrAAType::kCoverage) ||
           (aa1 == GrAAType::kCoverage && aa2 == GrAAType::kNone);
}

bool GrSimpleMeshDrawOpHelper::isCompatible(const GrSimpleMeshDrawOpHelper& that,
                                            const GrCaps& caps, const SkRect& thisBounds,
                                            const SkRect& thatBounds, bool noneAsCoverageAA) const {
    if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) {
        return false;
    }
    if (fProcessors) {
        if (*fProcessors != *that.fProcessors) {
            return false;
        }
    }
    bool result = fPipelineFlags == that.fPipelineFlags && (fAAType == that.fAAType ||
            (noneAsCoverageAA && none_as_coverage_aa_compatible(this->aaType(), that.aaType())));
    SkASSERT(!result || fCompatibleWithCoverageAsAlpha == that.fCompatibleWithCoverageAsAlpha);
    SkASSERT(!result || fUsesLocalCoords == that.fUsesLocalCoords);
    return result;
}

GrProcessorSet::Analysis GrSimpleMeshDrawOpHelper::finalizeProcessors(
        const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
        GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage,
        SkPMColor4f* geometryColor, bool* wideColor) {
    GrProcessorAnalysisColor color = *geometryColor;
    auto result = this->finalizeProcessors(
            caps, clip, hasMixedSampledCoverage, clampType, geometryCoverage, &color);
    color.isConstant(geometryColor);
    if (wideColor) {
        *wideColor = SkPMColor4fNeedsWideColor(*geometryColor, clampType, caps);
    }
    return result;
}

GrProcessorSet::Analysis GrSimpleMeshDrawOpHelper::finalizeProcessors(
        const GrCaps& caps, const GrAppliedClip* clip, const GrUserStencilSettings* userStencil,
        bool hasMixedSampledCoverage, GrClampType clampType,
        GrProcessorAnalysisCoverage geometryCoverage, GrProcessorAnalysisColor* geometryColor) {
    SkDEBUGCODE(fDidAnalysis = true);
    GrProcessorSet::Analysis analysis;
    if (fProcessors) {
        GrProcessorAnalysisCoverage coverage = geometryCoverage;
        if (GrProcessorAnalysisCoverage::kNone == coverage) {
            coverage = clip->numClipCoverageFragmentProcessors()
                               ? GrProcessorAnalysisCoverage::kSingleChannel
                               : GrProcessorAnalysisCoverage::kNone;
        }
        SkPMColor4f overrideColor;
        analysis = fProcessors->finalize(*geometryColor, coverage, clip, userStencil,
                                         hasMixedSampledCoverage, caps, clampType, &overrideColor);
        if (analysis.inputColorIsOverridden()) {
            *geometryColor = overrideColor;
        }
    } else {
        analysis = GrProcessorSet::EmptySetAnalysis();
    }
    fUsesLocalCoords = analysis.usesLocalCoords();
    fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
    return analysis;
}

void GrSimpleMeshDrawOpHelper::executeDrawsAndUploads(
        const GrOp* op, GrOpFlushState* flushState, const SkRect& chainBounds) {
    if (fProcessors) {
        flushState->executeDrawsAndUploadsForMeshDrawOp(
                op, chainBounds, std::move(*fProcessors), fPipelineFlags);
    } else {
        flushState->executeDrawsAndUploadsForMeshDrawOp(
                op, chainBounds, GrProcessorSet::MakeEmptySet(), fPipelineFlags);
    }
}

#ifdef SK_DEBUG
static void dump_pipeline_flags(GrPipeline::InputFlags flags, SkString* result) {
    if (GrPipeline::InputFlags::kNone != flags) {
        if (flags & GrPipeline::InputFlags::kSnapVerticesToPixelCenters) {
            result->append("Snap vertices to pixel center.\n");
        }
        if (flags & GrPipeline::InputFlags::kHWAntialias) {
            result->append("HW Antialiasing enabled.\n");
        }
        return;
    }
    result->append("No pipeline flags\n");
}

SkString GrSimpleMeshDrawOpHelper::dumpInfo() const {
    const GrProcessorSet& processors = fProcessors ? *fProcessors : GrProcessorSet::EmptySet();
    SkString result = processors.dumpProcessors();
    result.append("AA Type: ");
    switch (this->aaType()) {
        case GrAAType::kNone:
            result.append(" none\n");
            break;
        case GrAAType::kCoverage:
            result.append(" coverage\n");
            break;
        case GrAAType::kMSAA:
            result.append(" msaa\n");
            break;
    }
    dump_pipeline_flags(fPipelineFlags, &result);
    return result;
}
#endif

GrSimpleMeshDrawOpHelperWithStencil::GrSimpleMeshDrawOpHelperWithStencil(
        const MakeArgs& args, GrAAType aaType, const GrUserStencilSettings* stencilSettings,
        InputFlags inputFlags)
        : INHERITED(args, aaType, inputFlags)
        , fStencilSettings(stencilSettings ? stencilSettings : &GrUserStencilSettings::kUnused) {}

GrDrawOp::FixedFunctionFlags GrSimpleMeshDrawOpHelperWithStencil::fixedFunctionFlags() const {
    GrDrawOp::FixedFunctionFlags flags = INHERITED::fixedFunctionFlags();
    if (fStencilSettings != &GrUserStencilSettings::kUnused) {
        flags |= GrDrawOp::FixedFunctionFlags::kUsesStencil;
    }
    return flags;
}

GrProcessorSet::Analysis GrSimpleMeshDrawOpHelperWithStencil::finalizeProcessors(
        const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
        GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage,
        SkPMColor4f* geometryColor, bool* wideColor) {
    GrProcessorAnalysisColor color = *geometryColor;
    auto result = this->finalizeProcessors(
            caps, clip, hasMixedSampledCoverage, clampType, geometryCoverage, &color);
    color.isConstant(geometryColor);
    if (wideColor) {
        *wideColor = SkPMColor4fNeedsWideColor(*geometryColor, clampType, caps);
    }
    return result;
}

bool GrSimpleMeshDrawOpHelperWithStencil::isCompatible(
        const GrSimpleMeshDrawOpHelperWithStencil& that, const GrCaps& caps,
        const SkRect& thisBounds, const SkRect& thatBounds, bool noneAsCoverageAA) const {
    return INHERITED::isCompatible(that, caps, thisBounds, thatBounds, noneAsCoverageAA) &&
           fStencilSettings == that.fStencilSettings;
}

void GrSimpleMeshDrawOpHelperWithStencil::executeDrawsAndUploads(
        const GrOp* op, GrOpFlushState* flushState, const SkRect& chainBounds) {
    if (fProcessors) {
        flushState->executeDrawsAndUploadsForMeshDrawOp(
                op, chainBounds, std::move(*fProcessors), fPipelineFlags, fStencilSettings);
    } else {
        flushState->executeDrawsAndUploadsForMeshDrawOp(
                op, chainBounds, GrProcessorSet::MakeEmptySet(), fPipelineFlags, fStencilSettings);
    }
}

#ifdef SK_DEBUG
SkString GrSimpleMeshDrawOpHelperWithStencil::dumpInfo() const {
    SkString result = INHERITED::dumpInfo();
    result.appendf("Stencil settings: %s\n", (fStencilSettings ? "yes" : "no"));
    return result;
}
#endif