/*
 * 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 "SkColorPriv.h"
#include "SkOverdrawMode.h"
#include "SkString.h"
#include "SkXfermode.h"

#if SK_SUPPORT_GPU
#include "GrFragmentProcessor.h"
#include "GrInvariantOutput.h"
#include "GrXferProcessor.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLXferProcessor.h"

 ///////////////////////////////////////////////////////////////////////////////
 // Fragment Processor
 ///////////////////////////////////////////////////////////////////////////////

class GLOverdrawFP;

class GrOverdrawFP : public GrFragmentProcessor {
public:
    static const GrFragmentProcessor* Create(const GrFragmentProcessor* dst) {
        return new GrOverdrawFP(dst);
    }

    ~GrOverdrawFP() override { }

    const char* name() const override { return "Overdraw"; }

    SkString dumpInfo() const override {
        SkString str;
        return str;
    }

private:
    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;

    void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder* b) const override;

    bool onIsEqual(const GrFragmentProcessor&) const override { return true; }

    void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
        inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
    }

    GrOverdrawFP(const GrFragmentProcessor* dst) {
        this->initClassID<GrOverdrawFP>();

        SkASSERT(dst);
        SkDEBUGCODE(int dstIndex = )this->registerChildProcessor(dst);
        SkASSERT(0 == dstIndex);
    }

    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
    typedef GrFragmentProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

static void add_overdraw_code(GrGLSLFragmentBuilder* fragBuilder,
                              const char* dstColor,
                              const char* outputColor) {

    static const GrGLSLShaderVar gColorTableArgs[] = {
        // TODO: once kInt_GrSLType lands - switch this over
        GrGLSLShaderVar("index", kFloat_GrSLType),
    };
    SkString colorTableFuncName;

    // The 'colorTable' function exists to work around older GLSL's prohibition
    // of initialized arrays. It takes an integer index and just returns the
    // corresponding color.
   fragBuilder->emitFunction(kVec4f_GrSLType,
                             "colorTable",
                             SK_ARRAY_COUNT(gColorTableArgs),
                             gColorTableArgs,
                             "if (index < 1.5) { return vec4(0.5,   0.617, 1.0,   1.0); }"
                             "if (index < 2.5) { return vec4(0.664, 0.723, 0.83,  1.0); }"
                             "if (index < 3.5) { return vec4(0.832, 0.762, 0.664, 1.0); }"
                             "if (index < 4.5) { return vec4(1,     0.75,  0.496, 1.0); }"
                             "if (index < 5.5) { return vec4(1,     0.723, 0.332, 1.0); }"
                             "if (index < 6.5) { return vec4(1,     0.645, 0.164, 1.0); }"
                             "if (index < 7.5) { return vec4(1,     0.527, 0,     1.0); }"
                             "if (index < 8.5) { return vec4(1,     0.371, 0,     1.0); }"
                             "if (index < 9.5) { return vec4(1,     0.195, 0,     1.0); }"
                             "return vec4(1,     0,     0,     1.0);",
                             &colorTableFuncName);

    fragBuilder->codeAppend("int nextIdx;");
    fragBuilder->codeAppendf("vec4 dst = %s;", dstColor);
    fragBuilder->codeAppend("if (dst.r < 0.25) { nextIdx = 1; }");
    // cap 'idx' at 10
    fragBuilder->codeAppend("else if (dst.g < 0.0977) { nextIdx = 10; }");
    fragBuilder->codeAppend("else if (dst.b > 0.08) { nextIdx = 8 - int(6.0 * dst.b + 0.5); }");
    fragBuilder->codeAppend("else { nextIdx = 11 - int(5.7 * dst.g + 0.5); }");
    fragBuilder->codeAppendf("%s = %s(float(nextIdx));", outputColor, colorTableFuncName.c_str());
}

class GLOverdrawFP : public GrGLSLFragmentProcessor {
public:
    void emitCode(EmitArgs& args) override {
        GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
        SkString dstColor("dstColor");
        this->emitChild(0, nullptr, &dstColor, args);

        add_overdraw_code(fragBuilder, dstColor.c_str(), args.fOutputColor);
    }

    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) { }

private:
    typedef GrGLSLFragmentProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

GrGLSLFragmentProcessor* GrOverdrawFP::onCreateGLSLInstance() const {
    return new GLOverdrawFP;
}

void GrOverdrawFP::onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
    GLOverdrawFP::GenKey(*this, caps, b);
}

const GrFragmentProcessor* GrOverdrawFP::TestCreate(GrProcessorTestData* d) {
    SkAutoTUnref<const GrFragmentProcessor> dst(GrProcessorUnitTest::CreateChildFP(d));
    return new GrOverdrawFP(dst);
}

GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrOverdrawFP);

///////////////////////////////////////////////////////////////////////////////
// Xfer Processor
///////////////////////////////////////////////////////////////////////////////

class OverdrawXP : public GrXferProcessor {
public:
    OverdrawXP(const DstTexture* dstTexture, bool hasMixedSamples)
        : INHERITED(dstTexture, true, hasMixedSamples) {
        this->initClassID<OverdrawXP>();
    }

    const char* name() const override { return "Overdraw"; }

    GrGLSLXferProcessor* createGLSLInstance() const override;

private:
    GrXferProcessor::OptFlags onGetOptimizations(const GrPipelineOptimizations& optimizations,
                                                 bool doesStencilWrite,
                                                 GrColor* overrideColor,
                                                 const GrCaps& caps) const override {
        // We never look at the color input
        return GrXferProcessor::kIgnoreColor_OptFlag;
    }

    void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override;

    bool onIsEqual(const GrXferProcessor&) const override { return true; }

    typedef GrXferProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

class GLOverdrawXP : public GrGLSLXferProcessor {
public:
    GLOverdrawXP(const OverdrawXP&) { }

    ~GLOverdrawXP() override {}

    static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) { }

private:
    void emitBlendCodeForDstRead(GrGLSLXPFragmentBuilder* fragBuilder,
                                 GrGLSLUniformHandler* uniformHandler,
                                 const char* srcColor,
                                 const char* srcCoverage,
                                 const char* dstColor,
                                 const char* outColor,
                                 const char* outColorSecondary,
                                 const GrXferProcessor& proc) override {
        add_overdraw_code(fragBuilder, dstColor, outColor);

        // Apply coverage.
        INHERITED::DefaultCoverageModulation(fragBuilder, srcCoverage, dstColor, outColor,
                                             outColorSecondary, proc);
    }

    void onSetData(const GrGLSLProgramDataManager&, const GrXferProcessor&) override { };

    typedef GrGLSLXferProcessor INHERITED;
};

///////////////////////////////////////////////////////////////////////////////

void OverdrawXP::onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const {
    GLOverdrawXP::GenKey(*this, caps, b);
}

GrGLSLXferProcessor* OverdrawXP::createGLSLInstance() const { return new GLOverdrawXP(*this); }

///////////////////////////////////////////////////////////////////////////////
class GrOverdrawXPFactory : public GrXPFactory {
public:
    static GrXPFactory* Create() { return new GrOverdrawXPFactory(); }

    void getInvariantBlendedColor(const GrProcOptInfo& colorPOI,
                                  GrXPFactory::InvariantBlendedColor* blendedColor) const override {
        blendedColor->fWillBlendWithDst = true;
        blendedColor->fKnownColorFlags = kNone_GrColorComponentFlags;
    }

private:
    GrOverdrawXPFactory() {
        this->initClassID<GrOverdrawXPFactory>();
    }

    GrXferProcessor* onCreateXferProcessor(const GrCaps& caps,
                                           const GrPipelineOptimizations& optimizations,
                                           bool hasMixedSamples,
                                           const DstTexture* dstTexture) const override {
        return new OverdrawXP(dstTexture, hasMixedSamples);
    }

    bool onWillReadDstColor(const GrCaps& caps,
                            const GrPipelineOptimizations& optimizations,
                            bool hasMixedSamples) const override {
        return true;
    }

    bool onIsEqual(const GrXPFactory& xpfBase) const override { return true; }

    GR_DECLARE_XP_FACTORY_TEST;

    typedef GrXPFactory INHERITED;
};

GR_DEFINE_XP_FACTORY_TEST(GrOverdrawXPFactory);

const GrXPFactory* GrOverdrawXPFactory::TestCreate(GrProcessorTestData* d) {
    return GrOverdrawXPFactory::Create();
}
#endif

///////////////////////////////////////////////////////////////////////////////
class SkOverdrawXfermode : public SkXfermode {
public:
    static SkXfermode* Create() {
        return new SkOverdrawXfermode;
    }

    SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override {
        // This table encodes the color progression of the overdraw visualization
        static const SkPMColor gTable[] = {
            SkPackARGB32(0x00, 0x00, 0x00, 0x00),
            SkPackARGB32(0xFF, 128, 158, 255),
            SkPackARGB32(0xFF, 170, 185, 212),
            SkPackARGB32(0xFF, 213, 195, 170),
            SkPackARGB32(0xFF, 255, 192, 127),
            SkPackARGB32(0xFF, 255, 185, 85),
            SkPackARGB32(0xFF, 255, 165, 42),
            SkPackARGB32(0xFF, 255, 135, 0),
            SkPackARGB32(0xFF, 255,  95, 0),
            SkPackARGB32(0xFF, 255,  50, 0),
            SkPackARGB32(0xFF, 255,  0, 0)
        };


        int nextIdx;
        if (SkColorGetR(dst) < 64) { // dst color is the 0th color so the next color is 1
            nextIdx = 1;
        } else if (SkColorGetG(dst) < 25) { // dst color is the 10th color so cap there
            nextIdx = 10;
        } else if ((SkColorGetB(dst)+21)/42 > 0) { // dst color is one of 1-6
            nextIdx = 8 - (SkColorGetB(dst)+21)/42;
        } else { // dst color is between 7 and 9
            nextIdx = 11 - (SkColorGetG(dst)+22)/45;
        }
        SkASSERT(nextIdx < (int)SK_ARRAY_COUNT(gTable));

        return gTable[nextIdx];
    }

    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkOverdrawXfermode)

#if SK_SUPPORT_GPU
    const GrFragmentProcessor* getFragmentProcessorForImageFilter(
                                                const GrFragmentProcessor* dst) const override {
        return GrOverdrawFP::Create(dst);
    }

    GrXPFactory* asXPFactory() const override {
        return GrOverdrawXPFactory::Create();
    }
#endif

#ifndef SK_IGNORE_TO_STRING
    void toString(SkString* str) const override { str->set("SkOverdrawXfermode"); }
#endif

private:
    friend class SkOverdrawMode;

    void flatten(SkWriteBuffer& buffer) const override { }

    typedef SkXfermode INHERITED;
};

SkFlattenable* SkOverdrawXfermode::CreateProc(SkReadBuffer& buffer) {
    return Create();
}

SkXfermode* SkOverdrawMode::Create() { return new SkOverdrawXfermode; }

SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkOverdrawMode)
    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOverdrawXfermode)
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END