package me.august.lumen.compile.parser.ast.expr;

import me.august.lumen.common.BytecodeUtil;
import me.august.lumen.compile.analyze.var.LocalVariable;
import me.august.lumen.compile.analyze.var.VariableReference;
import me.august.lumen.compile.codegen.BuildContext;
import me.august.lumen.compile.parser.ast.Popable;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

// We're pretending to use the POP instruction, because
// the same rules apply to incrementing.
public class IncrementExpr implements Expression, Popable {

    public enum Op {
        INC(1), DEC(-1);

        Op(int delta) {
            this.delta = delta;
        }

        private int delta;
    }

    private VariableExpression value;
    private Op op;
    private boolean postfix;
    private boolean shouldGen;

    public IncrementExpr(Expression value, Op op, boolean postfix) {
        if (!(value instanceof VariableExpression))
            throw new IllegalArgumentException("Invalid type, expected variable");

        this.value   = (VariableExpression) value;
        this.op      = op;
        this.postfix = postfix;
    }

    @Override
    public Type expressionType() {
        return value.expressionType();
    }

    @Override
    public void generate(MethodVisitor visitor, BuildContext context) {
        if (!BytecodeUtil.isNumeric(expressionType()) || expressionType().getSort() > Type.INT)
            throw new RuntimeException("Incompatible type: " + expressionType());

        // If it's postfix we load the value
        // *before* incrementing, so the un-incremented
        // value is pushed onto the stack.
        if (postfix && shouldGen) {
            value.generate(visitor, context);
        }

        // if it's a local variable, use IINC instruction
        VariableReference var = value.getVariableReference();
        if (var instanceof LocalVariable) {
            LocalVariable local = (LocalVariable) var;
            visitor.visitIincInsn(local.getIndex(), op.delta);
        } else { // otherwise: load, add 1, save
            var.generateSetCode(visitor, (m) -> {
                var.generateGetCode(m);
                BytecodeUtil.pushInt(m, op.delta);
                m.visitInsn(Opcodes.IADD);
            });
        }

        // If it's not postfix we load the value
        // *after* incrementing, so the incremented
        // value is pushed onto the stack.
        if (!postfix && shouldGen) {
            value.generate(visitor, context);
        }
    }

    @Override
    public void shouldPop(boolean pop) {
        shouldGen = pop;
    }

    @Override
    public boolean shouldPop() {
        return shouldGen;
    }

    @Override
    public Expression[] getChildren() {
        return new Expression[]{value};
    }
}

@@ -2621,16 +2621,17 @@
 ldGen = 
+!
 pop;%0A   

Fix increment expression generation