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

#include "src/sksl/SkSLOutputStream.h"
#include "src/sksl/ir/SkSLProgram.h"

namespace SkSL {

/**
 * Abstract superclass of all code generators, which take a Program as input and produce code as
 * output.
 */
class CodeGenerator {
public:
    CodeGenerator(const Context* context, const Program* program, OutputStream* out)
    : fContext(*context)
    , fProgram(*program)
    , fOut(out) {}

    virtual ~CodeGenerator() {}

    virtual bool generateCode() = 0;

    // Intended for use by AutoOutputStream.
    OutputStream* outputStream() { return fOut; }
    void setOutputStream(OutputStream* output) { fOut = output; }

protected:
#if defined(SK_USE_LEGACY_MIPMAP_LOD_BIAS)
    static constexpr float kSharpenTexturesBias = -.5f;
#else
    // For SkMipmapMode::kLinear we want a bias such that when the unbiased LOD value is
    // midway between levels we select just the larger level, i.e. a bias of -.5. However, using
    // this bias with kNearest mode with a draw that is a perfect power of two downscale puts us
    // right on the rounding edge where it could go up or down depending on the particular GPU.
    // Experimentally we found that at -.49 most iOS devices (iPhone 7, 8, and iPad Pro
    // [PowerVRGT7800 version]) all round to the level twice as big as the device space footprint
    // for some such draws in our unit tests on GLES. However, the iPhone 11 still fails and so
    // we are using -.475. They do not at -.48. All other GPUs passed tests with -.499. Though, at
    // this time the bias is not implemented in the MSL codegen and so iOS/Metal was not tested.
    static constexpr float kSharpenTexturesBias = -.475f;
#endif

    const Context& fContext;
    const Program& fProgram;
    OutputStream* fOut;
};

class AutoOutputStream {
public:
    // Maintains the current indentation level while writing to the new output stream.
    AutoOutputStream(CodeGenerator* codeGen, OutputStream* newOutput)
            : fCodeGen(codeGen)
            , fOldOutput(codeGen->outputStream()) {
        fCodeGen->setOutputStream(newOutput);
    }
    // Resets the indentation when entering the scope, and restores it when leaving.
    AutoOutputStream(CodeGenerator* codeGen, OutputStream* newOutput, int *indentationPtr)
            : fCodeGen(codeGen)
            , fOldOutput(codeGen->outputStream())
            , fIndentationPtr(indentationPtr)
            , fOldIndentation(indentationPtr ? *indentationPtr : 0) {
        fCodeGen->setOutputStream(newOutput);
        *fIndentationPtr = 0;
    }
    ~AutoOutputStream() {
        fCodeGen->setOutputStream(fOldOutput);
        if (fIndentationPtr) {
            *fIndentationPtr = fOldIndentation;
        }
    }

private:
    CodeGenerator* fCodeGen = nullptr;
    OutputStream* fOldOutput = nullptr;
    int *fIndentationPtr = nullptr;
    int fOldIndentation = 0;
};

}  // namespace SkSL

#endif
