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

#include "GrFragmentProcessor.h"
#include "GrCoordTransform.h"
#include "GrInvariantOutput.h"
#include "GrProcOptInfo.h"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.h"
#include "glsl/GrGLSLProgramDataManager.h"
#include "glsl/GrGLSLUniformHandler.h"
#include "effects/GrConstColorProcessor.h"
#include "effects/GrXfermodeFragmentProcessor.h"

GrFragmentProcessor::~GrFragmentProcessor() {
    // If we got here then our ref count must have reached zero, so we will have converted refs
    // to pending executions for all children.
    for (int i = 0; i < fChildProcessors.count(); ++i) {
        fChildProcessors[i]->completedExecution();
    }
}

bool GrFragmentProcessor::isEqual(const GrFragmentProcessor& that,
                                  bool ignoreCoordTransforms) const {
    if (this->classID() != that.classID() ||
        !this->hasSameTextureAccesses(that)) {
        return false;
    }
    if (ignoreCoordTransforms) {
        if (this->numTransforms() != that.numTransforms()) {
            return false;
        }
    } else if (!this->hasSameTransforms(that)) {
        return false;
    }
    if (!this->onIsEqual(that)) {
        return false;
    }
    if (this->numChildProcessors() != that.numChildProcessors()) {
        return false;
    }
    for (int i = 0; i < this->numChildProcessors(); ++i) {
        if (!this->childProcessor(i).isEqual(that.childProcessor(i), ignoreCoordTransforms)) {
            return false;
        }
    }
    return true;
}

GrGLSLFragmentProcessor* GrFragmentProcessor::createGLSLInstance() const {
    GrGLSLFragmentProcessor* glFragProc = this->onCreateGLSLInstance();
    glFragProc->fChildProcessors.push_back_n(fChildProcessors.count());
    for (int i = 0; i < fChildProcessors.count(); ++i) {
        glFragProc->fChildProcessors[i] = fChildProcessors[i]->createGLSLInstance();
    }
    return glFragProc;
}

void GrFragmentProcessor::addTextureAccess(const GrTextureAccess* textureAccess) {
    // Can't add texture accesses after registering any children since their texture accesses have
    // already been bubbled up into our fTextureAccesses array
    SkASSERT(fChildProcessors.empty());

    INHERITED::addTextureAccess(textureAccess);
    fNumTexturesExclChildren++;
}

void GrFragmentProcessor::addCoordTransform(const GrCoordTransform* transform) {
    // Can't add transforms after registering any children since their transforms have already been
    // bubbled up into our fCoordTransforms array
    SkASSERT(fChildProcessors.empty());

    fCoordTransforms.push_back(transform);
    fUsesLocalCoords = fUsesLocalCoords || transform->sourceCoords() == kLocal_GrCoordSet;
    SkDEBUGCODE(transform->setInProcessor();)
    fNumTransformsExclChildren++;
}

int GrFragmentProcessor::registerChildProcessor(const GrFragmentProcessor* child) {
    // Append the child's transforms to our transforms array and the child's textures array to our
    // textures array
    if (!child->fCoordTransforms.empty()) {
        fCoordTransforms.push_back_n(child->fCoordTransforms.count(),
                                     child->fCoordTransforms.begin());
    }
    if (!child->fTextureAccesses.empty()) {
        fTextureAccesses.push_back_n(child->fTextureAccesses.count(),
                                     child->fTextureAccesses.begin());
    }

    int index = fChildProcessors.count();
    fChildProcessors.push_back(SkRef(child));

    if (child->willReadFragmentPosition()) {
        this->setWillReadFragmentPosition();
    }

    if (child->usesLocalCoords()) {
        fUsesLocalCoords = true;
    }

    return index;
}

void GrFragmentProcessor::notifyRefCntIsZero() const {
    // See comment above GrProgramElement for a detailed explanation of why we do this.
    for (int i = 0; i < fChildProcessors.count(); ++i) {
        fChildProcessors[i]->addPendingExecution();
        fChildProcessors[i]->unref();
    }
}

bool GrFragmentProcessor::hasSameTransforms(const GrFragmentProcessor& that) const {
    if (this->numTransforms() != that.numTransforms()) {
        return false;
    }
    int count = this->numTransforms();
    for (int i = 0; i < count; ++i) {
        if (this->coordTransform(i) != that.coordTransform(i)) {
            return false;
        }
    }
    return true;
}

const GrFragmentProcessor* GrFragmentProcessor::MulOutputByInputAlpha(
    const GrFragmentProcessor* fp) {
    if (!fp) {
        return nullptr;
    }
    return GrXfermodeFragmentProcessor::CreateFromDstProcessor(fp, SkXfermode::kDstIn_Mode);
}

const GrFragmentProcessor* GrFragmentProcessor::MulOutputByInputUnpremulColor(
    const GrFragmentProcessor* fp) {

    class PremulFragmentProcessor : public GrFragmentProcessor {
    public:
        PremulFragmentProcessor(const GrFragmentProcessor* processor) {
            this->initClassID<PremulFragmentProcessor>();
            this->registerChildProcessor(processor);
        }

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

    private:
        GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
            class GLFP : public GrGLSLFragmentProcessor {
            public:
                void emitCode(EmitArgs& args) override {
                    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
                    this->emitChild(0, nullptr, args);
                    fragBuilder->codeAppendf("%s.rgb *= %s.rgb;", args.fOutputColor,
                                                                args.fInputColor);
                    fragBuilder->codeAppendf("%s *= %s.a;", args.fOutputColor, args.fInputColor);
                }
            };
            return new GLFP;
        }

        void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override {}

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

        void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
            // TODO: Add a helper to GrInvariantOutput that handles multiplying by color with flags?
            if (!(inout->validFlags() & kA_GrColorComponentFlag)) {
                inout->setToUnknown(GrInvariantOutput::kWill_ReadInput);
                return;
            }

            GrInvariantOutput childOutput(GrColor_WHITE, kRGBA_GrColorComponentFlags, false);
            this->childProcessor(0).computeInvariantOutput(&childOutput);

            if (0 == GrColorUnpackA(inout->color()) || 0 == GrColorUnpackA(childOutput.color())) {
                inout->mulByKnownFourComponents(0x0);
                return;
            }
            GrColorComponentFlags commonFlags = childOutput.validFlags() & inout->validFlags();
            GrColor c0 = GrPremulColor(inout->color());
            GrColor c1 = childOutput.color();
            GrColor color = 0x0;
            if (commonFlags & kR_GrColorComponentFlag) {
                color |= SkMulDiv255Round(GrColorUnpackR(c0), GrColorUnpackR(c1)) <<
                    GrColor_SHIFT_R;
            }
            if (commonFlags & kG_GrColorComponentFlag) {
                color |= SkMulDiv255Round(GrColorUnpackG(c0), GrColorUnpackG(c1)) <<
                    GrColor_SHIFT_G;
            }
            if (commonFlags & kB_GrColorComponentFlag) {
                color |= SkMulDiv255Round(GrColorUnpackB(c0), GrColorUnpackB(c1)) <<
                    GrColor_SHIFT_B;
            }
            inout->setToOther(commonFlags, color, GrInvariantOutput::kWill_ReadInput);
        }
    };
    if (!fp) {
        return nullptr;
    }
    return new PremulFragmentProcessor(fp);
}

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

const GrFragmentProcessor* GrFragmentProcessor::OverrideInput(const GrFragmentProcessor* fp,
                                                              GrColor color) {
    class ReplaceInputFragmentProcessor : public GrFragmentProcessor {
    public:
        ReplaceInputFragmentProcessor(const GrFragmentProcessor* child, GrColor color)
            : fColor(color) {
            this->initClassID<ReplaceInputFragmentProcessor>();
            this->registerChildProcessor(child);
        }

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

        GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
            class GLFP : public GrGLSLFragmentProcessor {
            public:
                GLFP() : fHaveSetColor(false) {}
                void emitCode(EmitArgs& args) override {
                    const char* colorName;
                    fColorUni = args.fUniformHandler->addUniform(kFragment_GrShaderFlag,
                                                                 kVec4f_GrSLType,
                                                                 kDefault_GrSLPrecision,
                                                                 "Color", &colorName);
                    this->emitChild(0, colorName, args);
                }

            private:
                void onSetData(const GrGLSLProgramDataManager& pdman,
                               const GrProcessor& fp) override {
                    GrColor color = fp.cast<ReplaceInputFragmentProcessor>().fColor;
                    if (!fHaveSetColor || color != fPreviousColor) {
                        static const float scale = 1.f / 255.f;
                        float floatColor[4] = {
                            GrColorUnpackR(color) * scale,
                            GrColorUnpackG(color) * scale,
                            GrColorUnpackB(color) * scale,
                            GrColorUnpackA(color) * scale,
                        };
                        pdman.set4fv(fColorUni, 1, floatColor);
                        fPreviousColor = color;
                        fHaveSetColor = true;
                    }
                }

                GrGLSLProgramDataManager::UniformHandle fColorUni;
                bool    fHaveSetColor;
                GrColor fPreviousColor;
            };

            return new GLFP;
        }

    private:
        void onGetGLSLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override
        {}

        bool onIsEqual(const GrFragmentProcessor& that) const override {
            return fColor == that.cast<ReplaceInputFragmentProcessor>().fColor;
        }

        void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
            inout->setToOther(kRGBA_GrColorComponentFlags, fColor,
                              GrInvariantOutput::kWillNot_ReadInput);
            this->childProcessor(0).computeInvariantOutput(inout);
        }

        GrColor fColor;
    };

    GrInvariantOutput childOut(0x0, kNone_GrColorComponentFlags, false);
    fp->computeInvariantOutput(&childOut);
    if (childOut.willUseInputColor()) {
        return new ReplaceInputFragmentProcessor(fp, color);
    } else {
        return SkRef(fp);
    }
}

const GrFragmentProcessor* GrFragmentProcessor::RunInSeries(const GrFragmentProcessor* series[],
                                                            int cnt) {
    class SeriesFragmentProcessor : public GrFragmentProcessor {
    public:
        SeriesFragmentProcessor(const GrFragmentProcessor* children[], int cnt){
            SkASSERT(cnt > 1);
            this->initClassID<SeriesFragmentProcessor>();
            for (int i = 0; i < cnt; ++i) {
                this->registerChildProcessor(children[i]);
            }
        }

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

        GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
            class GLFP : public GrGLSLFragmentProcessor {
            public:
                void emitCode(EmitArgs& args) override {
                    SkString input(args.fInputColor);
                    for (int i = 0; i < this->numChildProcessors() - 1; ++i) {
                        SkString temp;
                        temp.printf("out%d", i);
                        this->emitChild(i, input.c_str(), &temp, args);
                        input = temp;
                    }
                    // Last guy writes to our output variable.
                    this->emitChild(this->numChildProcessors() - 1, input.c_str(), args);
                }
            };
            return new GLFP;
        }

    private:
        void onGetGLSLProcessorKey(const GrGLSLCaps&, GrProcessorKeyBuilder*) const override {}

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

        void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
            GrProcOptInfo info;
            SkTDArray<const GrFragmentProcessor*> children;
            children.setCount(this->numChildProcessors());
            for (int i = 0; i < children.count(); ++i) {
                children[i] = &this->childProcessor(i);
            }
            info.calcWithInitialValues(children.begin(), children.count(), inout->color(),
                                       inout->validFlags(), false, false);
            for (int i = 0; i < this->numChildProcessors(); ++i) {
                this->childProcessor(i).computeInvariantOutput(inout);
            }
        }
    };

    if (!cnt) {
        return nullptr;
    }

    // Run the through the series, do the invariant output processing, and look for eliminations.
    SkTDArray<const GrFragmentProcessor*> replacementSeries;
    SkAutoTUnref<const GrFragmentProcessor> colorFP;
    GrProcOptInfo info;

    info.calcWithInitialValues(series, cnt, 0x0, kNone_GrColorComponentFlags, false, false);
    if (kRGBA_GrColorComponentFlags == info.validFlags()) {
        return GrConstColorProcessor::Create(info.color(),
                                             GrConstColorProcessor::kIgnore_InputMode);
    } else {
        int firstIdx = info.firstEffectiveProcessorIndex();
        cnt -= firstIdx;
        if (firstIdx > 0 && info.inputColorIsUsed()) {
            colorFP.reset(GrConstColorProcessor::Create(info.inputColorToFirstEffectiveProccesor(),
                                                        GrConstColorProcessor::kIgnore_InputMode));
            cnt += 1;
            replacementSeries.setCount(cnt);
            replacementSeries[0] = colorFP;
            for (int i = 0; i < cnt - 1; ++i) {
                replacementSeries[i + 1] = series[firstIdx + i];
            }
            series = replacementSeries.begin();
        } else {
            series += firstIdx;
            cnt -= firstIdx;
        }
    }

    if (1 == cnt) {
        return SkRef(series[0]);
    } else {
        return new SeriesFragmentProcessor(series, cnt);
    }
}