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

#ifndef SKSL_STANDALONE

#include "SkSLInterpreter.h"
#include "ir/SkSLBinaryExpression.h"
#include "ir/SkSLExpressionStatement.h"
#include "ir/SkSLForStatement.h"
#include "ir/SkSLFunctionCall.h"
#include "ir/SkSLFunctionReference.h"
#include "ir/SkSLIfStatement.h"
#include "ir/SkSLIndexExpression.h"
#include "ir/SkSLPostfixExpression.h"
#include "ir/SkSLPrefixExpression.h"
#include "ir/SkSLProgram.h"
#include "ir/SkSLStatement.h"
#include "ir/SkSLTernaryExpression.h"
#include "ir/SkSLVarDeclarations.h"
#include "ir/SkSLVarDeclarationsStatement.h"
#include "ir/SkSLVariableReference.h"
#include "SkRasterPipeline.h"
#include "../jumper/SkJumper.h"

namespace SkSL {

void Interpreter::run() {
    for (const auto& e : *fProgram) {
        if (ProgramElement::kFunction_Kind == e.fKind) {
            const FunctionDefinition& f = (const FunctionDefinition&) e;
            if ("appendStages" == f.fDeclaration.fName) {
                this->run(f);
                return;
            }
        }
    }
    SkASSERT(false);
}

static int SizeOf(const Type& type) {
    return 1;
}

void Interpreter::run(const FunctionDefinition& f) {
    fVars.emplace_back();
    StackIndex current = (StackIndex) fStack.size();
    for (int i = f.fDeclaration.fParameters.size() - 1; i >= 0; --i) {
        current -= SizeOf(f.fDeclaration.fParameters[i]->fType);
        fVars.back()[f.fDeclaration.fParameters[i]] = current;
    }
    fCurrentIndex.push_back({ f.fBody.get(), 0 });
    while (fCurrentIndex.size()) {
        this->runStatement();
    }
}

void Interpreter::push(Value value) {
    fStack.push_back(value);
}

Interpreter::Value Interpreter::pop() {
    auto iter = fStack.end() - 1;
    Value result = *iter;
    fStack.erase(iter);
    return result;
}

 Interpreter::StackIndex Interpreter::stackAlloc(int count) {
    int result = fStack.size();
    for (int i = 0; i < count; ++i) {
        fStack.push_back(Value((int) 0xDEADBEEF));
    }
    return result;
}

void Interpreter::runStatement() {
    const Statement& stmt = *fCurrentIndex.back().fStatement;
    const size_t index = fCurrentIndex.back().fIndex;
    fCurrentIndex.pop_back();
    switch (stmt.fKind) {
        case Statement::kBlock_Kind: {
            const Block& b = (const Block&) stmt;
            if (!b.fStatements.size()) {
                break;
            }
            SkASSERT(index < b.fStatements.size());
            if (index < b.fStatements.size() - 1) {
                fCurrentIndex.push_back({ &b, index + 1 });
            }
            fCurrentIndex.push_back({ b.fStatements[index].get(), 0 });
            break;
        }
        case Statement::kBreak_Kind:
            SkASSERT(index == 0);
            abort();
        case Statement::kContinue_Kind:
            SkASSERT(index == 0);
            abort();
        case Statement::kDiscard_Kind:
            SkASSERT(index == 0);
            abort();
        case Statement::kDo_Kind:
            abort();
        case Statement::kExpression_Kind:
            SkASSERT(index == 0);
            this->evaluate(*((const ExpressionStatement&) stmt).fExpression);
            break;
        case Statement::kFor_Kind: {
            ForStatement& f = (ForStatement&) stmt;
            switch (index) {
                case 0:
                    // initializer
                    fCurrentIndex.push_back({ &f, 1 });
                    if (f.fInitializer) {
                        fCurrentIndex.push_back({ f.fInitializer.get(), 0 });
                    }
                    break;
                case 1:
                    // test & body
                    if (f.fTest && !evaluate(*f.fTest).fBool) {
                        break;
                    } else {
                        fCurrentIndex.push_back({ &f, 2 });
                        fCurrentIndex.push_back({ f.fStatement.get(), 0 });
                    }
                    break;
                case 2:
                    // next
                    if (f.fNext) {
                        this->evaluate(*f.fNext);
                    }
                    fCurrentIndex.push_back({ &f, 1 });
                    break;
                default:
                    SkASSERT(false);
            }
            break;
        }
        case Statement::kGroup_Kind:
            abort();
        case Statement::kIf_Kind: {
            IfStatement& i = (IfStatement&) stmt;
            if (evaluate(*i.fTest).fBool) {
                fCurrentIndex.push_back({ i.fIfTrue.get(), 0 });
            } else if (i.fIfFalse) {
                fCurrentIndex.push_back({ i.fIfFalse.get(), 0 });
            }
            break;
        }
        case Statement::kNop_Kind:
            SkASSERT(index == 0);
            break;
        case Statement::kReturn_Kind:
            SkASSERT(index == 0);
            abort();
        case Statement::kSwitch_Kind:
            abort();
        case Statement::kVarDeclarations_Kind:
            SkASSERT(index == 0);
            for (const auto& decl :((const VarDeclarationsStatement&) stmt).fDeclaration->fVars) {
                const Variable* var = ((VarDeclaration&) *decl).fVar;
                StackIndex pos = this->stackAlloc(SizeOf(var->fType));
                fVars.back()[var] = pos;
                if (var->fInitialValue) {
                    fStack[pos] = this->evaluate(*var->fInitialValue);
                }
            }
            break;
        case Statement::kWhile_Kind:
            abort();
        default:
            abort();
    }
}

static Interpreter::TypeKind type_kind(const Type& type) {
    if (type.fName == "int") {
        return Interpreter::kInt_TypeKind;
    } else if (type.fName == "float") {
        return Interpreter::kFloat_TypeKind;
    }
    ABORT("unsupported type: %s\n", type.description().c_str());
}

Interpreter::StackIndex Interpreter::getLValue(const Expression& expr) {
    switch (expr.fKind) {
        case Expression::kFieldAccess_Kind:
            break;
        case Expression::kIndex_Kind: {
            const IndexExpression& idx = (const IndexExpression&) expr;
            return this->evaluate(*idx.fBase).fInt + this->evaluate(*idx.fIndex).fInt;
        }
        case Expression::kSwizzle_Kind:
            break;
        case Expression::kVariableReference_Kind:
            SkASSERT(fVars.size());
            SkASSERT(fVars.back().find(&((VariableReference&) expr).fVariable) !=
                   fVars.back().end());
            return fVars.back()[&((VariableReference&) expr).fVariable];
        case Expression::kTernary_Kind: {
            const TernaryExpression& t = (const TernaryExpression&) expr;
            return this->getLValue(this->evaluate(*t.fTest).fBool ? *t.fIfTrue : *t.fIfFalse);
        }
        case Expression::kTypeReference_Kind:
            break;
        default:
            break;
    }
    ABORT("unsupported lvalue");
}

struct CallbackCtx : public SkJumper_CallbackCtx {
    Interpreter* fInterpreter;
    const FunctionDefinition* fFunction;
};

static void do_callback(SkJumper_CallbackCtx* raw, int activePixels) {
    CallbackCtx& ctx = (CallbackCtx&) *raw;
    for (int i = 0; i < activePixels; ++i) {
        ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 0]));
        ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 1]));
        ctx.fInterpreter->push(Interpreter::Value(ctx.rgba[i * 4 + 2]));
        ctx.fInterpreter->run(*ctx.fFunction);
        ctx.read_from[i * 4 + 2] = ctx.fInterpreter->pop().fFloat;
        ctx.read_from[i * 4 + 1] = ctx.fInterpreter->pop().fFloat;
        ctx.read_from[i * 4 + 0] = ctx.fInterpreter->pop().fFloat;
    }
}

void Interpreter::appendStage(const AppendStage& a) {
    switch (a.fStage) {
        case SkRasterPipeline::matrix_4x5: {
            SkASSERT(a.fArguments.size() == 1);
            StackIndex transpose = evaluate(*a.fArguments[0]).fInt;
            fPipeline.append(SkRasterPipeline::matrix_4x5, &fStack[transpose]);
            break;
        }
        case SkRasterPipeline::callback: {
            SkASSERT(a.fArguments.size() == 1);
            CallbackCtx* ctx = new CallbackCtx();
            ctx->fInterpreter = this;
            ctx->fn = do_callback;
            for (const auto& e : *fProgram) {
                if (ProgramElement::kFunction_Kind == e.fKind) {
                    const FunctionDefinition& f = (const FunctionDefinition&) e;
                    if (&f.fDeclaration ==
                                      ((const FunctionReference&) *a.fArguments[0]).fFunctions[0]) {
                        ctx->fFunction = &f;
                    }
                }
            }
            fPipeline.append(SkRasterPipeline::callback, ctx);
            break;
        }
        default:
            fPipeline.append(a.fStage);
    }
}

Interpreter::Value Interpreter::call(const FunctionCall& c) {
    abort();
}

Interpreter::Value Interpreter::evaluate(const Expression& expr) {
    switch (expr.fKind) {
        case Expression::kAppendStage_Kind:
            this->appendStage((const AppendStage&) expr);
            return Value((int) 0xDEADBEEF);
        case Expression::kBinary_Kind: {
            #define ARITHMETIC(op) {                               \
                Value left = this->evaluate(*b.fLeft);             \
                Value right = this->evaluate(*b.fRight);           \
                switch (type_kind(b.fLeft->fType)) {               \
                    case kFloat_TypeKind:                          \
                        return Value(left.fFloat op right.fFloat); \
                    case kInt_TypeKind:                            \
                        return Value(left.fInt op right.fInt);     \
                    default:                                       \
                        abort();                                   \
                }                                                  \
            }
            #define BITWISE(op) {                                  \
                Value left = this->evaluate(*b.fLeft);             \
                Value right = this->evaluate(*b.fRight);           \
                switch (type_kind(b.fLeft->fType)) {               \
                    case kInt_TypeKind:                            \
                        return Value(left.fInt op right.fInt);     \
                    default:                                       \
                        abort();                                   \
                }                                                  \
            }
            #define LOGIC(op) {                                    \
                Value left = this->evaluate(*b.fLeft);             \
                Value right = this->evaluate(*b.fRight);           \
                switch (type_kind(b.fLeft->fType)) {               \
                    case kFloat_TypeKind:                          \
                        return Value(left.fFloat op right.fFloat); \
                    case kInt_TypeKind:                            \
                        return Value(left.fInt op right.fInt);     \
                    default:                                       \
                        abort();                                   \
                }                                                  \
            }
            #define COMPOUND_ARITHMETIC(op) {                      \
                StackIndex left = this->getLValue(*b.fLeft);       \
                Value right = this->evaluate(*b.fRight);           \
                Value result = fStack[left];                       \
                switch (type_kind(b.fLeft->fType)) {               \
                    case kFloat_TypeKind:                          \
                        result.fFloat op right.fFloat;             \
                        break;                                     \
                    case kInt_TypeKind:                            \
                        result.fInt op right.fInt;                 \
                        break;                                     \
                    default:                                       \
                        abort();                                   \
                }                                                  \
                fStack[left] = result;                             \
                return result;                                     \
            }
            #define COMPOUND_BITWISE(op) {                         \
                StackIndex left = this->getLValue(*b.fLeft);       \
                Value right = this->evaluate(*b.fRight);           \
                Value result = fStack[left];                       \
                switch (type_kind(b.fLeft->fType)) {               \
                    case kInt_TypeKind:                            \
                        result.fInt op right.fInt;                 \
                        break;                                     \
                    default:                                       \
                        abort();                                   \
                }                                                  \
                fStack[left] = result;                             \
                return result;                                     \
            }
            const BinaryExpression& b = (const BinaryExpression&) expr;
            switch (b.fOperator) {
                case Token::PLUS:       ARITHMETIC(+)
                case Token::MINUS:      ARITHMETIC(-)
                case Token::STAR:       ARITHMETIC(*)
                case Token::SLASH:      ARITHMETIC(/)
                case Token::BITWISEAND: BITWISE(&)
                case Token::BITWISEOR:  BITWISE(|)
                case Token::BITWISEXOR: BITWISE(^)
                case Token::LT:         LOGIC(<)
                case Token::GT:         LOGIC(>)
                case Token::LTEQ:       LOGIC(<=)
                case Token::GTEQ:       LOGIC(>=)
                case Token::LOGICALAND: {
                    Value result = this->evaluate(*b.fLeft);
                    if (result.fBool) {
                        result = this->evaluate(*b.fRight);
                    }
                    return result;
                }
                case Token::LOGICALOR: {
                    Value result = this->evaluate(*b.fLeft);
                    if (!result.fBool) {
                        result = this->evaluate(*b.fRight);
                    }
                    return result;
                }
                case Token::EQ: {
                    StackIndex left = this->getLValue(*b.fLeft);
                    Value right = this->evaluate(*b.fRight);
                    fStack[left] = right;
                    return right;
                }
                case Token::PLUSEQ:       COMPOUND_ARITHMETIC(+=)
                case Token::MINUSEQ:      COMPOUND_ARITHMETIC(-=)
                case Token::STAREQ:       COMPOUND_ARITHMETIC(*=)
                case Token::SLASHEQ:      COMPOUND_ARITHMETIC(/=)
                case Token::BITWISEANDEQ: COMPOUND_BITWISE(&=)
                case Token::BITWISEOREQ:  COMPOUND_BITWISE(|=)
                case Token::BITWISEXOREQ: COMPOUND_BITWISE(^=)
                default:
                    ABORT("unsupported operator: %s\n", expr.description().c_str());
            }
            break;
        }
        case Expression::kBoolLiteral_Kind:
            return Value(((const BoolLiteral&) expr).fValue);
        case Expression::kConstructor_Kind:
            break;
        case Expression::kIntLiteral_Kind:
            return Value((int) ((const IntLiteral&) expr).fValue);
        case Expression::kFieldAccess_Kind:
            break;
        case Expression::kFloatLiteral_Kind:
            return Value((float) ((const FloatLiteral&) expr).fValue);
        case Expression::kFunctionCall_Kind:
            return this->call((const FunctionCall&) expr);
        case Expression::kIndex_Kind: {
            const IndexExpression& idx = (const IndexExpression&) expr;
            StackIndex pos = this->evaluate(*idx.fBase).fInt +
                             this->evaluate(*idx.fIndex).fInt;
            return fStack[pos];
        }
        case Expression::kPrefix_Kind: {
            const PrefixExpression& p = (const PrefixExpression&) expr;
            switch (p.fOperator) {
                case Token::MINUS: {
                    Value base = this->evaluate(*p.fOperand);
                    switch (type_kind(p.fType)) {
                        case kFloat_TypeKind:
                            return Value(-base.fFloat);
                        case kInt_TypeKind:
                            return Value(-base.fInt);
                        default:
                            abort();
                    }
                }
                case Token::LOGICALNOT: {
                    Value base = this->evaluate(*p.fOperand);
                    return Value(!base.fBool);
                }
                default:
                    abort();
            }
        }
        case Expression::kPostfix_Kind: {
            const PostfixExpression& p = (const PostfixExpression&) expr;
            StackIndex lvalue = this->getLValue(*p.fOperand);
            Value result = fStack[lvalue];
            switch (type_kind(p.fType)) {
                case kFloat_TypeKind:
                    if (Token::PLUSPLUS == p.fOperator) {
                        ++fStack[lvalue].fFloat;
                    } else {
                        SkASSERT(Token::MINUSMINUS == p.fOperator);
                        --fStack[lvalue].fFloat;
                    }
                    break;
                case kInt_TypeKind:
                    if (Token::PLUSPLUS == p.fOperator) {
                        ++fStack[lvalue].fInt;
                    } else {
                        SkASSERT(Token::MINUSMINUS == p.fOperator);
                        --fStack[lvalue].fInt;
                    }
                    break;
                default:
                    abort();
            }
            return result;
        }
        case Expression::kSetting_Kind:
            break;
        case Expression::kSwizzle_Kind:
            break;
        case Expression::kVariableReference_Kind:
            SkASSERT(fVars.size());
            SkASSERT(fVars.back().find(&((VariableReference&) expr).fVariable) !=
                   fVars.back().end());
            return fStack[fVars.back()[&((VariableReference&) expr).fVariable]];
        case Expression::kTernary_Kind: {
            const TernaryExpression& t = (const TernaryExpression&) expr;
            return this->evaluate(this->evaluate(*t.fTest).fBool ? *t.fIfTrue : *t.fIfFalse);
        }
        case Expression::kTypeReference_Kind:
            break;
        default:
            break;
    }
    ABORT("unsupported expression: %s\n", expr.description().c_str());
}

} // namespace

#endif
