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

#include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"

#include "include/private/SkSLProgramElement.h"
#include "include/private/SkSLStatement.h"
#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLOperators.h"
#include "src/sksl/SkSLStringStream.h"
#include "src/sksl/ir/SkSLBinaryExpression.h"
#include "src/sksl/ir/SkSLChildCall.h"
#include "src/sksl/ir/SkSLConstructor.h"
#include "src/sksl/ir/SkSLConstructorArrayCast.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLExpressionStatement.h"
#include "src/sksl/ir/SkSLFieldAccess.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLFunctionCall.h"
#include "src/sksl/ir/SkSLFunctionDeclaration.h"
#include "src/sksl/ir/SkSLFunctionDefinition.h"
#include "src/sksl/ir/SkSLFunctionPrototype.h"
#include "src/sksl/ir/SkSLIfStatement.h"
#include "src/sksl/ir/SkSLIndexExpression.h"
#include "src/sksl/ir/SkSLPostfixExpression.h"
#include "src/sksl/ir/SkSLPrefixExpression.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLStructDefinition.h"
#include "src/sksl/ir/SkSLSwizzle.h"
#include "src/sksl/ir/SkSLTernaryExpression.h"
#include "src/sksl/ir/SkSLVarDeclarations.h"
#include "src/sksl/ir/SkSLVariableReference.h"

#include <unordered_map>

#if defined(SKSL_STANDALONE) || SK_SUPPORT_GPU

namespace SkSL {
namespace PipelineStage {

class PipelineStageCodeGenerator {
public:
    PipelineStageCodeGenerator(const Program& program,
                               const char* sampleCoords,
                               const char* inputColor,
                               const char* destColor,
                               Callbacks* callbacks)
            : fProgram(program)
            , fSampleCoords(sampleCoords)
            , fInputColor(inputColor)
            , fDestColor(destColor)
            , fCallbacks(callbacks) {}

    void generateCode();

private:
    using Precedence = Operator::Precedence;

    void write(skstd::string_view s);
    void writeLine(skstd::string_view s = skstd::string_view());

    String typeName(const Type& type);
    void writeType(const Type& type);

    String functionName(const FunctionDeclaration& decl);
    void writeFunction(const FunctionDefinition& f);
    void writeFunctionPrototype(const FunctionPrototype& f);

    String modifierString(const Modifiers& modifiers);

    // Handles arrays correctly, eg: `float x[2]`
    String typedVariable(const Type& type, skstd::string_view name);

    void writeVarDeclaration(const VarDeclaration& var);
    void writeGlobalVarDeclaration(const GlobalVarDeclaration& g);
    void writeStructDefinition(const StructDefinition& s);

    void writeExpression(const Expression& expr, Precedence parentPrecedence);
    void writeChildCall(const ChildCall& c);
    void writeFunctionCall(const FunctionCall& c);
    void writeAnyConstructor(const AnyConstructor& c, Precedence parentPrecedence);
    void writeFieldAccess(const FieldAccess& f);
    void writeSwizzle(const Swizzle& swizzle);
    void writeBinaryExpression(const BinaryExpression& b, Precedence parentPrecedence);
    void writeTernaryExpression(const TernaryExpression& t, Precedence parentPrecedence);
    void writeIndexExpression(const IndexExpression& expr);
    void writePrefixExpression(const PrefixExpression& p, Precedence parentPrecedence);
    void writePostfixExpression(const PostfixExpression& p, Precedence parentPrecedence);
    void writeVariableReference(const VariableReference& ref);

    void writeStatement(const Statement& s);
    void writeBlock(const Block& b);
    void writeIfStatement(const IfStatement& stmt);
    void writeDoStatement(const DoStatement& d);
    void writeForStatement(const ForStatement& f);
    void writeReturnStatement(const ReturnStatement& r);

    void writeProgramElement(const ProgramElement& e);

    struct AutoOutputBuffer {
        AutoOutputBuffer(PipelineStageCodeGenerator* generator) : fGenerator(generator) {
            fOldBuffer = fGenerator->fBuffer;
            fGenerator->fBuffer = &fBuffer;
        }

        ~AutoOutputBuffer() {
            fGenerator->fBuffer = fOldBuffer;
        }

        PipelineStageCodeGenerator* fGenerator;
        StringStream*               fOldBuffer;
        StringStream                fBuffer;
    };

    const Program& fProgram;
    const char*    fSampleCoords;
    const char*    fInputColor;
    const char*    fDestColor;
    Callbacks*     fCallbacks;

    std::unordered_map<const Variable*, String>            fVariableNames;
    std::unordered_map<const FunctionDeclaration*, String> fFunctionNames;
    std::unordered_map<const Type*, String>                fStructNames;

    StringStream* fBuffer = nullptr;
    bool          fCastReturnsToHalf = false;
};


void PipelineStageCodeGenerator::write(skstd::string_view s) {
    fBuffer->write(s.data(), s.length());
}

void PipelineStageCodeGenerator::writeLine(skstd::string_view s) {
    fBuffer->write(s.data(), s.length());
    fBuffer->writeText("\n");
}

void PipelineStageCodeGenerator::writeChildCall(const ChildCall& c) {
    const ExpressionArray& arguments = c.arguments();
    SkASSERT(arguments.size() >= 1);
    int index = 0;
    bool found = false;
    for (const ProgramElement* p : fProgram.elements()) {
        if (p->is<GlobalVarDeclaration>()) {
            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
            if (&decl.var() == &c.child()) {
                found = true;
            } else if (decl.var().type().isEffectChild()) {
                ++index;
            }
        }
        if (found) {
            break;
        }
    }
    SkASSERT(found);

    // Shaders require a coordinate argument. Color filters require a color argument.
    // Blenders require two color arguments.
    String sampleOutput;
    {
        AutoOutputBuffer exprBuffer(this);
        this->writeExpression(*arguments[0], Precedence::kSequence);

        switch (c.child().type().typeKind()) {
            case Type::TypeKind::kShader: {
                SkASSERT(arguments.size() == 1);
                SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fFloat2);
                sampleOutput = fCallbacks->sampleShader(index, exprBuffer.fBuffer.str());
                break;
            }
            case Type::TypeKind::kColorFilter: {
                SkASSERT(arguments.size() == 1);
                SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fHalf4 ||
                         arguments[0]->type() == *fProgram.fContext->fTypes.fFloat4);
                sampleOutput = fCallbacks->sampleColorFilter(index, exprBuffer.fBuffer.str());
                break;
            }
            case Type::TypeKind::kBlender: {
                SkASSERT(arguments.size() == 2);
                SkASSERT(arguments[0]->type() == *fProgram.fContext->fTypes.fHalf4 ||
                         arguments[0]->type() == *fProgram.fContext->fTypes.fFloat4);
                SkASSERT(arguments[1]->type() == *fProgram.fContext->fTypes.fHalf4 ||
                         arguments[1]->type() == *fProgram.fContext->fTypes.fFloat4);

                AutoOutputBuffer exprBuffer2(this);
                this->writeExpression(*arguments[1], Precedence::kSequence);

                sampleOutput = fCallbacks->sampleBlender(index, exprBuffer.fBuffer.str(),
                                                                exprBuffer2.fBuffer.str());
                break;
            }
            default: {
                SkDEBUGFAILF("cannot sample from type '%s'",
                             c.child().type().description().c_str());
            }
        }
    }
    this->write(sampleOutput);
    return;
}

void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
    const FunctionDeclaration& function = c.function();

    if (function.isBuiltin()) {
        this->write(function.name());
    } else {
        this->write(this->functionName(function));
    }

    this->write("(");
    const char* separator = "";
    for (const auto& arg : c.arguments()) {
        this->write(separator);
        separator = ", ";
        this->writeExpression(*arg, Precedence::kSequence);
    }
    this->write(")");
}

void PipelineStageCodeGenerator::writeVariableReference(const VariableReference& ref) {
    const Variable* var = ref.variable();
    const Modifiers& modifiers = var->modifiers();

    if (modifiers.fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN) {
        this->write(fSampleCoords);
        return;
    } else if (modifiers.fLayout.fBuiltin == SK_INPUT_COLOR_BUILTIN) {
        this->write(fInputColor);
        return;
    } else if (modifiers.fLayout.fBuiltin == SK_DEST_COLOR_BUILTIN) {
        this->write(fDestColor);
        return;
    }

    auto it = fVariableNames.find(var);
    if (it != fVariableNames.end()) {
        this->write(it->second);
    } else {
        this->write(var->name());
    }
}

void PipelineStageCodeGenerator::writeIfStatement(const IfStatement& stmt) {
    if (stmt.isStatic()) {
        this->write("@");
    }
    this->write("if (");
    this->writeExpression(*stmt.test(), Precedence::kTopLevel);
    this->write(") ");
    this->writeStatement(*stmt.ifTrue());
    if (stmt.ifFalse()) {
        this->write(" else ");
        this->writeStatement(*stmt.ifFalse());
    }
}

void PipelineStageCodeGenerator::writeReturnStatement(const ReturnStatement& r) {
    this->write("return");
    if (r.expression()) {
        this->write(" ");
        if (fCastReturnsToHalf) {
            this->write("half4(");
        }
        this->writeExpression(*r.expression(), Precedence::kTopLevel);
        if (fCastReturnsToHalf) {
            this->write(")");
        }
    }
    this->write(";");
}

String PipelineStageCodeGenerator::functionName(const FunctionDeclaration& decl) {
    if (decl.isMain()) {
        return String(decl.name());
    }

    auto it = fFunctionNames.find(&decl);
    if (it != fFunctionNames.end()) {
        return it->second;
    }

    String mangledName = fCallbacks->getMangledName(String(decl.name()).c_str());
    fFunctionNames.insert({&decl, mangledName});
    return mangledName;
}

void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) {
    AutoOutputBuffer body(this);

    // We allow public SkSL's main() to return half4 -or- float4 (ie vec4). When we emit
    // our code in the processor, the surrounding code is going to expect half4, so we
    // explicitly cast any returns (from main) to half4. This is only strictly necessary
    // if the return type is float4 - injecting it unconditionally reduces the risk of an
    // obscure bug.
    const FunctionDeclaration& decl = f.declaration();
    if (decl.isMain()) {
        fCastReturnsToHalf = true;
    }

    for (const std::unique_ptr<Statement>& stmt : f.body()->as<Block>().children()) {
        this->writeStatement(*stmt);
        this->writeLine();
    }

    if (decl.isMain()) {
        fCastReturnsToHalf = false;
    }

    // This is similar to decl.description(), but substitutes a mangled name, and handles modifiers
    // on the function (e.g. `inline`) and its parameters (e.g. `inout`).
    String declString =
            String::printf("%s%s%s %s(",
                           (decl.modifiers().fFlags & Modifiers::kInline_Flag) ? "inline " : "",
                           (decl.modifiers().fFlags & Modifiers::kNoInline_Flag) ? "noinline " : "",
                           this->typeName(decl.returnType()).c_str(),
                           this->functionName(decl).c_str());
    const char* separator = "";
    for (const Variable* p : decl.parameters()) {
        // TODO: Handle arrays
        declString.appendf("%s%s%s %s",
                           separator,
                           this->modifierString(p->modifiers()).c_str(),
                           this->typeName(p->type()).c_str(),
                           String(p->name()).c_str());
        separator = ", ";
    }
    declString.append(")");

    fCallbacks->defineFunction(declString.c_str(), body.fBuffer.str().c_str(), decl.isMain());
}

void PipelineStageCodeGenerator::writeFunctionPrototype(const FunctionPrototype& f) {
    const FunctionDeclaration& decl = f.declaration();
    (void)this->functionName(decl);
}

void PipelineStageCodeGenerator::writeGlobalVarDeclaration(const GlobalVarDeclaration& g) {
    const VarDeclaration& decl = g.declaration()->as<VarDeclaration>();
    const Variable& var = decl.var();

    if (var.isBuiltin() || var.type().isOpaque()) {
        // Don't re-declare these. (eg, sk_FragCoord, or fragmentProcessor children)
    } else if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
        String uniformName = fCallbacks->declareUniform(&decl);
        fVariableNames.insert({&var, std::move(uniformName)});
    } else {
        String mangledName = fCallbacks->getMangledName(String(var.name()).c_str());
        String declaration = this->modifierString(var.modifiers()) +
                             this->typedVariable(var.type(),
                                                 skstd::string_view(mangledName.c_str()));
        if (decl.value()) {
            AutoOutputBuffer outputToBuffer(this);
            this->writeExpression(*decl.value(), Precedence::kTopLevel);
            declaration += " = ";
            declaration += outputToBuffer.fBuffer.str();
        }
        declaration += ";\n";
        fCallbacks->declareGlobal(declaration.c_str());
        fVariableNames.insert({&var, std::move(mangledName)});
    }
}

void PipelineStageCodeGenerator::writeStructDefinition(const StructDefinition& s) {
    const Type& type = s.type();
    String mangledName = fCallbacks->getMangledName(String(type.name()).c_str());
    String definition = "struct " + mangledName + " {\n";
    for (const auto& f : type.fields()) {
        definition += this->typedVariable(*f.fType, f.fName) + ";\n";
    }
    definition += "};\n";
    fStructNames.insert({&type, std::move(mangledName)});
    fCallbacks->defineStruct(definition.c_str());
}

void PipelineStageCodeGenerator::writeProgramElement(const ProgramElement& e) {
    switch (e.kind()) {
        case ProgramElement::Kind::kGlobalVar:
            this->writeGlobalVarDeclaration(e.as<GlobalVarDeclaration>());
            break;
        case ProgramElement::Kind::kFunction:
            this->writeFunction(e.as<FunctionDefinition>());
            break;
        case ProgramElement::Kind::kFunctionPrototype:
            this->writeFunctionPrototype(e.as<FunctionPrototype>());
            break;
        case ProgramElement::Kind::kStructDefinition:
            this->writeStructDefinition(e.as<StructDefinition>());
            break;

        case ProgramElement::Kind::kExtension:
        case ProgramElement::Kind::kInterfaceBlock:
        case ProgramElement::Kind::kModifiers:
        default:
            SkDEBUGFAILF("unsupported program element %s\n", e.description().c_str());
            break;
    }
}

String PipelineStageCodeGenerator::typeName(const Type& type) {
    if (type.isArray()) {
        // This is necessary so that name mangling on arrays-of-structs works properly.
        String arrayName = this->typeName(type.componentType());
        arrayName.push_back('[');
        arrayName += to_string(type.columns());
        arrayName.push_back(']');
        return arrayName;
    }

    auto it = fStructNames.find(&type);
    return it != fStructNames.end() ? it->second : String(type.name());
}

void PipelineStageCodeGenerator::writeType(const Type& type) {
    this->write(this->typeName(type));
}

void PipelineStageCodeGenerator::writeExpression(const Expression& expr,
                                                 Precedence parentPrecedence) {
    switch (expr.kind()) {
        case Expression::Kind::kBinary:
            this->writeBinaryExpression(expr.as<BinaryExpression>(), parentPrecedence);
            break;
        case Expression::Kind::kBoolLiteral:
        case Expression::Kind::kFloatLiteral:
        case Expression::Kind::kIntLiteral:
            this->write(expr.description());
            break;
        case Expression::Kind::kChildCall:
            this->writeChildCall(expr.as<ChildCall>());
            break;
        case Expression::Kind::kConstructorArray:
        case Expression::Kind::kConstructorArrayCast:
        case Expression::Kind::kConstructorCompound:
        case Expression::Kind::kConstructorCompoundCast:
        case Expression::Kind::kConstructorDiagonalMatrix:
        case Expression::Kind::kConstructorMatrixResize:
        case Expression::Kind::kConstructorScalarCast:
        case Expression::Kind::kConstructorSplat:
        case Expression::Kind::kConstructorStruct:
            this->writeAnyConstructor(expr.asAnyConstructor(), parentPrecedence);
            break;
        case Expression::Kind::kFieldAccess:
            this->writeFieldAccess(expr.as<FieldAccess>());
            break;
        case Expression::Kind::kFunctionCall:
            this->writeFunctionCall(expr.as<FunctionCall>());
            break;
        case Expression::Kind::kPrefix:
            this->writePrefixExpression(expr.as<PrefixExpression>(), parentPrecedence);
            break;
        case Expression::Kind::kPostfix:
            this->writePostfixExpression(expr.as<PostfixExpression>(), parentPrecedence);
            break;
        case Expression::Kind::kSwizzle:
            this->writeSwizzle(expr.as<Swizzle>());
            break;
        case Expression::Kind::kVariableReference:
            this->writeVariableReference(expr.as<VariableReference>());
            break;
        case Expression::Kind::kTernary:
            this->writeTernaryExpression(expr.as<TernaryExpression>(), parentPrecedence);
            break;
        case Expression::Kind::kIndex:
            this->writeIndexExpression(expr.as<IndexExpression>());
            break;
        case Expression::Kind::kSetting:
        default:
            SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str());
            break;
    }
}

void PipelineStageCodeGenerator::writeAnyConstructor(const AnyConstructor& c,
                                                     Precedence parentPrecedence) {
    this->writeType(c.type());
    this->write("(");
    const char* separator = "";
    for (const auto& arg : c.argumentSpan()) {
        this->write(separator);
        separator = ", ";
        this->writeExpression(*arg, Precedence::kSequence);
    }
    this->write(")");
}

void PipelineStageCodeGenerator::writeIndexExpression(const IndexExpression& expr) {
    this->writeExpression(*expr.base(), Precedence::kPostfix);
    this->write("[");
    this->writeExpression(*expr.index(), Precedence::kTopLevel);
    this->write("]");
}

void PipelineStageCodeGenerator::writeFieldAccess(const FieldAccess& f) {
    if (f.ownerKind() == FieldAccess::OwnerKind::kDefault) {
        this->writeExpression(*f.base(), Precedence::kPostfix);
        this->write(".");
    }
    const Type& baseType = f.base()->type();
    this->write(baseType.fields()[f.fieldIndex()].fName);
}

void PipelineStageCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
    this->writeExpression(*swizzle.base(), Precedence::kPostfix);
    this->write(".");
    for (int c : swizzle.components()) {
        SkASSERT(c >= 0 && c <= 3);
        this->write(&("x\0y\0z\0w\0"[c * 2]));
    }
}

void PipelineStageCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                                       Precedence parentPrecedence) {
    const Expression& left = *b.left();
    const Expression& right = *b.right();
    Operator op = b.getOperator();

    Precedence precedence = op.getBinaryPrecedence();
    if (precedence >= parentPrecedence) {
        this->write("(");
    }
    this->writeExpression(left, precedence);
    this->write(" ");
    this->write(op.operatorName());
    this->write(" ");
    this->writeExpression(right, precedence);
    if (precedence >= parentPrecedence) {
        this->write(")");
    }
}

void PipelineStageCodeGenerator::writeTernaryExpression(const TernaryExpression& t,
                                                        Precedence parentPrecedence) {
    if (Precedence::kTernary >= parentPrecedence) {
        this->write("(");
    }
    this->writeExpression(*t.test(), Precedence::kTernary);
    this->write(" ? ");
    this->writeExpression(*t.ifTrue(), Precedence::kTernary);
    this->write(" : ");
    this->writeExpression(*t.ifFalse(), Precedence::kTernary);
    if (Precedence::kTernary >= parentPrecedence) {
        this->write(")");
    }
}

void PipelineStageCodeGenerator::writePrefixExpression(const PrefixExpression& p,
                                                       Precedence parentPrecedence) {
    if (Precedence::kPrefix >= parentPrecedence) {
        this->write("(");
    }
    this->write(p.getOperator().operatorName());
    this->writeExpression(*p.operand(), Precedence::kPrefix);
    if (Precedence::kPrefix >= parentPrecedence) {
        this->write(")");
    }
}

void PipelineStageCodeGenerator::writePostfixExpression(const PostfixExpression& p,
                                                        Precedence parentPrecedence) {
    if (Precedence::kPostfix >= parentPrecedence) {
        this->write("(");
    }
    this->writeExpression(*p.operand(), Precedence::kPostfix);
    this->write(p.getOperator().operatorName());
    if (Precedence::kPostfix >= parentPrecedence) {
        this->write(")");
    }
}

String PipelineStageCodeGenerator::modifierString(const Modifiers& modifiers) {
    String result;
    if (modifiers.fFlags & Modifiers::kConst_Flag) {
        result.append("const ");
    }

    if ((modifiers.fFlags & Modifiers::kIn_Flag) && (modifiers.fFlags & Modifiers::kOut_Flag)) {
        result.append("inout ");
    } else if (modifiers.fFlags & Modifiers::kIn_Flag) {
        result.append("in ");
    } else if (modifiers.fFlags & Modifiers::kOut_Flag) {
        result.append("out ");
    }

    return result;
}

String PipelineStageCodeGenerator::typedVariable(const Type& type, skstd::string_view name) {
    const Type& baseType = type.isArray() ? type.componentType() : type;

    String decl = this->typeName(baseType) + " " + name;
    if (type.isArray()) {
        decl += "[" + to_string(type.columns()) + "]";
    }
    return decl;
}

void PipelineStageCodeGenerator::writeVarDeclaration(const VarDeclaration& var) {
    this->write(this->modifierString(var.var().modifiers()));
    this->write(this->typedVariable(var.var().type(), var.var().name()));
    if (var.value()) {
        this->write(" = ");
        this->writeExpression(*var.value(), Precedence::kTopLevel);
    }
    this->write(";");
}

void PipelineStageCodeGenerator::writeStatement(const Statement& s) {
    switch (s.kind()) {
        case Statement::Kind::kBlock:
            this->writeBlock(s.as<Block>());
            break;
        case Statement::Kind::kBreak:
            this->write("break;");
            break;
        case Statement::Kind::kContinue:
            this->write("continue;");
            break;
        case Statement::Kind::kExpression:
            this->writeExpression(*s.as<ExpressionStatement>().expression(), Precedence::kTopLevel);
            this->write(";");
            break;
        case Statement::Kind::kDo:
            this->writeDoStatement(s.as<DoStatement>());
            break;
        case Statement::Kind::kFor:
            this->writeForStatement(s.as<ForStatement>());
            break;
        case Statement::Kind::kIf:
            this->writeIfStatement(s.as<IfStatement>());
            break;
        case Statement::Kind::kReturn:
            this->writeReturnStatement(s.as<ReturnStatement>());
            break;
        case Statement::Kind::kVarDeclaration:
            this->writeVarDeclaration(s.as<VarDeclaration>());
            break;
        case Statement::Kind::kDiscard:
        case Statement::Kind::kSwitch:
            SkDEBUGFAIL("Unsupported control flow");
            break;
        case Statement::Kind::kInlineMarker:
        case Statement::Kind::kNop:
            this->write(";");
            break;
        default:
            SkDEBUGFAILF("unsupported statement: %s", s.description().c_str());
            break;
    }
}

void PipelineStageCodeGenerator::writeBlock(const Block& b) {
    // Write scope markers if this block is a scope, or if the block is empty (since we need to emit
    // something here to make the code valid).
    bool isScope = b.isScope() || b.isEmpty();
    if (isScope) {
        this->writeLine("{");
    }
    for (const std::unique_ptr<Statement>& stmt : b.children()) {
        if (!stmt->isEmpty()) {
            this->writeStatement(*stmt);
            this->writeLine();
        }
    }
    if (isScope) {
        this->write("}");
    }
}

void PipelineStageCodeGenerator::writeDoStatement(const DoStatement& d) {
    this->write("do ");
    this->writeStatement(*d.statement());
    this->write(" while (");
    this->writeExpression(*d.test(), Precedence::kTopLevel);
    this->write(");");
    return;
}

void PipelineStageCodeGenerator::writeForStatement(const ForStatement& f) {
    // Emit loops of the form 'for(;test;)' as 'while(test)', which is probably how they started
    if (!f.initializer() && f.test() && !f.next()) {
        this->write("while (");
        this->writeExpression(*f.test(), Precedence::kTopLevel);
        this->write(") ");
        this->writeStatement(*f.statement());
        return;
    }

    this->write("for (");
    if (f.initializer() && !f.initializer()->isEmpty()) {
        this->writeStatement(*f.initializer());
    } else {
        this->write("; ");
    }
    if (f.test()) {
        this->writeExpression(*f.test(), Precedence::kTopLevel);
    }
    this->write("; ");
    if (f.next()) {
        this->writeExpression(*f.next(), Precedence::kTopLevel);
    }
    this->write(") ");
    this->writeStatement(*f.statement());
}

void PipelineStageCodeGenerator::generateCode() {
    // Write all the program elements except for functions.
    for (const ProgramElement* e : fProgram.elements()) {
        if (!e->is<FunctionDefinition>()) {
            this->writeProgramElement(*e);
        }
    }

    // We always place FunctionDefinition elements last, because the inliner likes to move function
    // bodies around. After inlining, code can inadvertently move upwards, above ProgramElements
    // that the code relies on.
    for (const ProgramElement* e : fProgram.elements()) {
        if (e->is<FunctionDefinition>()) {
            this->writeProgramElement(*e);
        }
    }
}

void ConvertProgram(const Program& program,
                    const char* sampleCoords,
                    const char* inputColor,
                    const char* destColor,
                    Callbacks* callbacks) {
    PipelineStageCodeGenerator generator(program, sampleCoords, inputColor, destColor, callbacks);
    generator.generateCode();
}

}  // namespace PipelineStage
}  // namespace SkSL

#endif
