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

#include "src/sksl/dsl/DSLCore.h"

#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLDefines.h"
#include "src/sksl/SkSLIRGenerator.h"
#include "src/sksl/dsl/priv/DSLWriter.h"
#include "src/sksl/ir/SkSLBreakStatement.h"
#include "src/sksl/ir/SkSLContinueStatement.h"
#include "src/sksl/ir/SkSLDiscardStatement.h"
#include "src/sksl/ir/SkSLDoStatement.h"
#include "src/sksl/ir/SkSLForStatement.h"
#include "src/sksl/ir/SkSLIfStatement.h"
#include "src/sksl/ir/SkSLReturnStatement.h"
#include "src/sksl/ir/SkSLSwizzle.h"

namespace SkSL {

namespace dsl {

#if SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)
void Start(SkSL::Compiler* compiler) {
    DSLWriter::SetInstance(std::make_unique<DSLWriter>(compiler));
}

void End() {
    SkASSERTF(!DSLWriter::InFragmentProcessor(),
              "more calls to StartFragmentProcessor than to EndFragmentProcessor");
    DSLWriter::SetInstance(nullptr);
}
#endif // SK_SUPPORT_GPU && !defined(SKSL_STANDALONE)

void SetErrorHandler(ErrorHandler* errorHandler) {
    DSLWriter::SetErrorHandler(errorHandler);
}

class DSLCore {
public:
    static DSLVar sk_FragColor() {
        return DSLVar("sk_FragColor");
    }

    static DSLVar sk_FragCoord() {
        return DSLVar("sk_FragCoord");
    }

    template <typename... Args>
    static DSLExpression Call(const char* name, Args... args) {
        SkSL::IRGenerator& ir = DSLWriter::IRGenerator();
        SkSL::ExpressionArray argArray;
        argArray.reserve_back(sizeof...(args));

        // in C++17, we could just do:
        // (argArray.push_back(args.release()), ...);
        int unused[] = {0, (DSLWriter::Ignore(argArray.push_back(args.release())), 0)...};
        static_cast<void>(unused);

        return ir.call(/*offset=*/-1, ir.convertIdentifier(-1, name), std::move(argArray));
    }

    static DSLStatement Break() {
        return std::unique_ptr<SkSL::Statement>(new SkSL::BreakStatement(/*offset=*/-1));
    }

    static DSLStatement Continue() {
        return std::unique_ptr<SkSL::Statement>(new SkSL::ContinueStatement(/*offset=*/-1));
    }

    static DSLStatement Declare(DSLVar& var, DSLExpression initialValue) {
        if (var.fConstVar) {
            DSLWriter::ReportError("Variable already declared");
            return DSLStatement();
        }
        SkASSERT(var.fVar);
        var.fConstVar = var.fVar.get();
        return DSLWriter::IRGenerator().convertVarDeclaration(std::move(var.fVar),
                                                              initialValue.release());
    }

    static DSLStatement Discard() {
        return std::unique_ptr<SkSL::Statement>(new SkSL::DiscardStatement(/*offset=*/-1));
    }

    static DSLStatement Do(DSLStatement stmt, DSLExpression test) {
        return DSLWriter::IRGenerator().convertDo(stmt.release(), test.release());
    }

    static DSLStatement For(DSLStatement initializer, DSLExpression test, DSLExpression next,
                            DSLStatement stmt) {
        return DSLWriter::IRGenerator().convertFor(/*offset=*/-1, initializer.release(),
                                                   test.release(), next.release(), stmt.release());
    }

    static DSLStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse) {
        return DSLWriter::IRGenerator().convertIf(/*offset=*/-1, /*isStatic=*/false, test.release(),
                                                  ifTrue.release(), ifFalse.release());
    }

    static DSLStatement Return(DSLExpression value) {
        // note that because Return is called before the function in which it resides exists, at
        // this point we do not know the function's return type. We therefore do not check for
        // errors, or coerce the value to the correct type, until the return statement is actually
        // added to a function
        std::unique_ptr<SkSL::Expression> expr = value.release();
        if (expr) {
            return std::unique_ptr<SkSL::Statement>(new ReturnStatement(std::move(expr)));
        } else {
            return std::unique_ptr<SkSL::Statement>(new ReturnStatement(/*offset=*/-1));
        }
    }

    static DSLExpression Swizzle(DSLExpression base, SkSL::SwizzleComponent::Type a) {
        return Swizzle::MakeWith01(DSLWriter::Context(), base.release(), ComponentArray{a});
    }

    static DSLExpression Swizzle(DSLExpression base,
                                 SkSL::SwizzleComponent::Type a,
                                 SkSL::SwizzleComponent::Type b) {
        return Swizzle::MakeWith01(DSLWriter::Context(), base.release(), ComponentArray{a, b});
    }

    static DSLExpression Swizzle(DSLExpression base,
                                 SkSL::SwizzleComponent::Type a,
                                 SkSL::SwizzleComponent::Type b,
                                 SkSL::SwizzleComponent::Type c) {
        return Swizzle::MakeWith01(DSLWriter::Context(), base.release(), ComponentArray{a, b, c});
    }

    static DSLExpression Swizzle(DSLExpression base,
                                 SkSL::SwizzleComponent::Type a,
                                 SkSL::SwizzleComponent::Type b,
                                 SkSL::SwizzleComponent::Type c,
                                 SkSL::SwizzleComponent::Type d) {
        return Swizzle::MakeWith01(DSLWriter::Context(), base.release(), ComponentArray{a,b,c,d});
    }

    static DSLExpression Select(DSLExpression test, DSLExpression ifTrue, DSLExpression ifFalse) {
        return DSLWriter::IRGenerator().convertTernaryExpression(test.release(), ifTrue.release(),
                                                                 ifFalse.release());
    }

    static DSLStatement While(DSLExpression test, DSLStatement stmt) {
        return DSLWriter::IRGenerator().convertWhile(/*offset=*/-1, test.release(), stmt.release());
    }
};

DSLVar sk_FragColor() {
    return DSLCore::sk_FragColor();
}

DSLVar sk_FragCoord() {
    return DSLCore::sk_FragCoord();
}

DSLStatement Break() {
    return DSLCore::Break();
}

DSLStatement Continue() {
    return DSLCore::Continue();
}

DSLStatement Declare(DSLVar& var, DSLExpression initialValue) {
    return DSLCore::Declare(var, std::move(initialValue));
}

DSLStatement Discard() {
    return DSLCore::Discard();
}

DSLStatement Do(DSLStatement stmt, DSLExpression test) {
    return DSLCore::Do(std::move(stmt), std::move(test));
}

DSLStatement For(DSLStatement initializer, DSLExpression test, DSLExpression next,
                 DSLStatement stmt) {
    return DSLCore::For(std::move(initializer), std::move(test), std::move(next), std::move(stmt));
}

DSLStatement If(DSLExpression test, DSLStatement ifTrue, DSLStatement ifFalse) {
    return DSLCore::If(std::move(test), std::move(ifTrue), std::move(ifFalse));
}

DSLStatement Return(DSLExpression expr) {
    return DSLCore::Return(std::move(expr));
}

DSLExpression Select(DSLExpression test, DSLExpression ifTrue, DSLExpression ifFalse) {
    return DSLCore::Select(std::move(test), std::move(ifTrue), std::move(ifFalse));
}

DSLStatement While(DSLExpression test, DSLStatement stmt) {
    return DSLCore::While(std::move(test), std::move(stmt));
}

DSLExpression Abs(DSLExpression x) {
    return DSLCore::Call("abs", std::move(x));
}

DSLExpression All(DSLExpression x) {
    return DSLCore::Call("all", std::move(x));
}

DSLExpression Any(DSLExpression x) {
    return DSLCore::Call("any", std::move(x));
}

DSLExpression Ceil(DSLExpression x) {
    return DSLCore::Call("ceil", std::move(x));
}

DSLExpression Clamp(DSLExpression x, DSLExpression min, DSLExpression max) {
    return DSLCore::Call("clamp", std::move(x), std::move(min), std::move(max));
}

DSLExpression Cos(DSLExpression x) {
    return DSLCore::Call("cos", std::move(x));
}

DSLExpression Cross(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("cross", std::move(x), std::move(y));
}

DSLExpression Degrees(DSLExpression x) {
    return DSLCore::Call("degrees", std::move(x));
}

DSLExpression Distance(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("distance", std::move(x), std::move(y));
}

DSLExpression Dot(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("dot", std::move(x), std::move(y));
}

DSLExpression Equal(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("equal", std::move(x), std::move(y));
}

DSLExpression Exp(DSLExpression x) {
    return DSLCore::Call("exp", std::move(x));
}

DSLExpression Exp2(DSLExpression x) {
    return DSLCore::Call("exp2", std::move(x));
}

DSLExpression Faceforward(DSLExpression n, DSLExpression i, DSLExpression nref) {
    return DSLCore::Call("faceforward", std::move(n), std::move(i), std::move(nref));
}

DSLExpression Fract(DSLExpression x) {
    return DSLCore::Call("fract", std::move(x));
}

DSLExpression Floor(DSLExpression x) {
    return DSLCore::Call("floor", std::move(x));
}

DSLExpression GreaterThan(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("greaterThan", std::move(x), std::move(y));
}

DSLExpression GreaterThanEqual(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("greaterThanEqual", std::move(x), std::move(y));
}

DSLExpression Inverse(DSLExpression x) {
    return DSLCore::Call("inverse", std::move(x));
}

DSLExpression Inversesqrt(DSLExpression x) {
    return DSLCore::Call("inversesqrt", std::move(x));
}

DSLExpression Length(DSLExpression x) {
    return DSLCore::Call("length", std::move(x));
}

DSLExpression LessThan(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("lessThan", std::move(x), std::move(y));
}

DSLExpression LessThanEqual(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("lessThanEqual", std::move(x), std::move(y));
}

DSLExpression Log(DSLExpression x) {
    return DSLCore::Call("log", std::move(x));
}

DSLExpression Log2(DSLExpression x) {
    return DSLCore::Call("log2", std::move(x));
}

DSLExpression Max(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("max", std::move(x), std::move(y));
}

DSLExpression Min(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("min", std::move(x), std::move(y));
}

DSLExpression Mix(DSLExpression x, DSLExpression y, DSLExpression a) {
    return DSLCore::Call("mix", std::move(x), std::move(y), std::move(a));
}

DSLExpression Mod(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("mod", std::move(x), std::move(y));
}

DSLExpression Normalize(DSLExpression x) {
    return DSLCore::Call("normalize", std::move(x));
}

DSLExpression NotEqual(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("notEqual", std::move(x), std::move(y));
}

DSLExpression Pow(DSLExpression x, DSLExpression y) {
    return DSLCore::Call("pow", std::move(x), std::move(y));
}

DSLExpression Radians(DSLExpression x) {
    return DSLCore::Call("radians", std::move(x));
}

DSLExpression Reflect(DSLExpression i, DSLExpression n) {
    return DSLCore::Call("reflect", std::move(i), std::move(n));
}

DSLExpression Refract(DSLExpression i, DSLExpression n, DSLExpression eta) {
    return DSLCore::Call("refract", std::move(i), std::move(n), std::move(eta));
}

DSLExpression Saturate(DSLExpression x) {
    return DSLCore::Call("saturate", std::move(x));
}

DSLExpression Sign(DSLExpression x) {
    return DSLCore::Call("sign", std::move(x));
}

DSLExpression Sin(DSLExpression x) {
    return DSLCore::Call("sin", std::move(x));
}

DSLExpression Smoothstep(DSLExpression edge1, DSLExpression edge2, DSLExpression x) {
    return DSLCore::Call("smoothstep", std::move(edge1), std::move(edge2), std::move(x));
}

DSLExpression Sqrt(DSLExpression x) {
    return DSLCore::Call("sqrt", std::move(x));
}

DSLExpression Step(DSLExpression edge, DSLExpression x) {
    return DSLCore::Call("step", std::move(edge), std::move(x));
}

DSLExpression Swizzle(DSLExpression base, SkSL::SwizzleComponent::Type a) {
    return DSLCore::Swizzle(std::move(base), a);
}

DSLExpression Swizzle(DSLExpression base,
                      SkSL::SwizzleComponent::Type a,
                      SkSL::SwizzleComponent::Type b) {
    return DSLCore::Swizzle(std::move(base), a, b);
}

DSLExpression Swizzle(DSLExpression base,
                      SkSL::SwizzleComponent::Type a,
                      SkSL::SwizzleComponent::Type b,
                      SkSL::SwizzleComponent::Type c) {
    return DSLCore::Swizzle(std::move(base), a, b, c);
}

DSLExpression Swizzle(DSLExpression base,
                      SkSL::SwizzleComponent::Type a,
                      SkSL::SwizzleComponent::Type b,
                      SkSL::SwizzleComponent::Type c,
                      SkSL::SwizzleComponent::Type d) {
    return DSLCore::Swizzle(std::move(base), a, b, c, d);
}

DSLExpression Tan(DSLExpression x) {
    return DSLCore::Call("tan", std::move(x));
}

DSLExpression Unpremul(DSLExpression x) {
    return DSLCore::Call("unpremul", std::move(x));
}

} // namespace dsl

} // namespace SkSL
