// See the file "COPYING" in the main distribution directory for copyright.

#include "pac_let.h"

#include "pac_expr.h"
#include "pac_exttype.h"
#include "pac_output.h"
#include "pac_type.h"

LetField::LetField(ID* id, Type* type, Expr* expr)
    : Field(LET_FIELD, TYPE_NOT_TO_BE_PARSED | CLASS_MEMBER | PUBLIC_READABLE, id, type), expr_(expr) {
    ASSERT(expr_);
}

LetField::~LetField() { delete expr_; }

bool LetField::DoTraverse(DataDepVisitor* visitor) { return Field::DoTraverse(visitor) && expr()->Traverse(visitor); }

bool LetField::RequiresAnalyzerContext() const {
    return Field::RequiresAnalyzerContext() || (expr() && expr()->RequiresAnalyzerContext());
}

void LetField::Prepare(Env* env) {
    if ( ! type_ ) {
        ASSERT(expr_);
        type_ = expr_->DataType(env);
        if ( type_ )
            type_ = type_->Clone();
        else
            type_ = extern_type_int->Clone();

        foreach (i, AttrList, attrs_)
            ProcessAttr(*i);
    }

    Field::Prepare(env);
    env->SetEvalMethod(id_, this);
}

void LetField::GenInitCode(Output* out_cc, Env* env) {
    int v;
    if ( expr_ && expr_->ConstFold(env, &v) ) {
        DEBUG_MSG("Folding const for `%s'\n", id_->Name());
        GenEval(out_cc, env);
    }
    else
        type_->GenInitCode(out_cc, env);
}

void LetField::GenParseCode(Output* out_cc, Env* env) {
    if ( env->Evaluated(id_) )
        return;

    if ( type_->attr_if_expr() ) {
        // A conditional field

        env->Evaluate(out_cc, type_->has_value_var());

        // force evaluation of IDs contained in this expr
        expr()->ForceIDEval(out_cc, env);

        out_cc->println("if ( %s ) {", env->RValue(type_->has_value_var()));
        out_cc->inc_indent();
    }

    out_cc->println("%s = %s;", env->LValue(id_), expr()->EvalExpr(out_cc, env));
    if ( ! env->Evaluated(id_) )
        env->SetEvaluated(id_);

    if ( type_->attr_if_expr() ) {
        out_cc->dec_indent();
        out_cc->println("}");
    }
}

void LetField::GenEval(Output* out_cc, Env* env) { GenParseCode(out_cc, env); }

LetDecl::LetDecl(ID* id, Type* type, Expr* expr) : Decl(id, LET), type_(type), expr_(expr) {
    if ( ! type_ ) {
        ASSERT(expr_);
        type_ = expr_->DataType(global_env());
        if ( type_ )
            type_ = type_->Clone();
        else
            type_ = extern_type_int->Clone();
    }

    Env* env = global_env();
    int c;
    if ( expr_ && expr_->ConstFold(env, &c) )
        env->AddConstID(id_, c, type);
    else
        env->AddID(id_, GLOBAL_VAR, type_);
}

LetDecl::~LetDecl() {
    delete type_;
    delete expr_;
}

void LetDecl::Prepare() {}

void LetDecl::GenForwardDeclaration(Output* out_h) {}

void LetDecl::GenCode(Output* out_h, Output* out_cc) {
    out_h->println("extern %s const %s;", type_->DataTypeStr().c_str(), global_env()->RValue(id_));
    GenEval(out_cc, global_env());
}

void LetDecl::GenEval(Output* out_cc, Env* /* env */) {
    Env* env = global_env();
    string tmp = strfmt("%s const", type_->DataTypeStr().c_str());
    out_cc->println("%s %s = %s;", tmp.c_str(), env->LValue(id_), expr_->EvalExpr(out_cc, env));

    if ( ! env->Evaluated(id_) )
        env->SetEvaluated(id_);
}
