// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.npc.util.expression.compile;

import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperatorTuple;
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperatorFunctionCall;
import java.text.ParseException;
import com.hypixel.hytale.server.npc.util.expression.compile.ast.ASTOperator;
import javax.annotation.Nullable;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.npc.util.expression.Expression;
import com.hypixel.hytale.server.npc.util.expression.ValueType;
import java.util.List;
import com.hypixel.hytale.server.npc.util.expression.Scope;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
import com.hypixel.hytale.server.npc.util.expression.compile.ast.AST;
import java.util.Stack;

public class CompileContext implements Parser.ParsedTokenConsumer
{
    private final Parser parser;
    private final Stack<AST> operandStack;
    @Nonnull
    private final ExecutionContext executionContext;
    private Scope scope;
    private List<ExecutionContext.Instruction> instructions;
    private ValueType resultType;
    
    public CompileContext() {
        this.parser = new Parser(Expression.getLexerInstance());
        this.operandStack = new Stack<AST>();
        this.resultType = ValueType.VOID;
        this.executionContext = new ExecutionContext();
    }
    
    public CompileContext(final Scope scope) {
        this();
        this.scope = scope;
    }
    
    public Scope getScope() {
        return this.scope;
    }
    
    @Nonnull
    public Stack<AST> getOperandStack() {
        return this.operandStack;
    }
    
    @Nonnull
    public ExecutionContext getExecutionContext() {
        return this.executionContext;
    }
    
    public ValueType compile(@Nonnull final String expression, final Scope compileScope, final boolean fullResolve) {
        return this.compile0(expression, compileScope, fullResolve, null);
    }
    
    public ValueType compile(@Nonnull final String expression, final Scope compileScope, final boolean fullResolve, final List<ExecutionContext.Instruction> instructions) {
        final ValueType valueType = this.compile0(expression, compileScope, fullResolve, instructions);
        this.setInstructions(null);
        return valueType;
    }
    
    protected ValueType compile0(@Nonnull final String expression, final Scope compileScope, final boolean fullResolve, final List<ExecutionContext.Instruction> instructions) {
        final Scope saveScope = this.scope;
        final Scope oldScope = this.executionContext.setScope(compileScope);
        this.scope = compileScope;
        this.setInstructions(instructions);
        this.compile(expression, fullResolve);
        this.executionContext.setScope(oldScope);
        this.scope = saveScope;
        return this.resultType;
    }
    
    public ValueType compile(@Nonnull final String expression, final boolean fullResolve) {
        try {
            this.operandStack.clear();
            this.resultType = ValueType.VOID;
            if (this.instructions == null) {
                this.instructions = new ObjectArrayList<ExecutionContext.Instruction>();
            }
            this.instructions.clear();
            this.parser.parse(expression, this);
            return this.resultType = this.operandStack.getFirst().genCode(this.instructions, fullResolve ? this.scope : null);
        }
        catch (final Throwable t) {
            throw new IllegalStateException("Error compiling expression '" + expression + "': " + t.getMessage(), t);
        }
    }
    
    public List<ExecutionContext.Instruction> getInstructions() {
        return this.instructions;
    }
    
    public void setInstructions(final List<ExecutionContext.Instruction> instructionList) {
        this.instructions = instructionList;
    }
    
    public ValueType getResultType() {
        return this.resultType;
    }
    
    @Nullable
    public ExecutionContext.Operand getAsOperand() {
        if (this.operandStack.size() != 1) {
            throw new IllegalArgumentException("There must be 1 element on stack to get as operand");
        }
        final AST ast = this.operandStack.getFirst();
        return ast.isConstant() ? ast.asOperand() : null;
    }
    
    public void checkResultType(final ValueType type) {
        if (type == ValueType.VOID) {
            throw new IllegalArgumentException("Result type can't be void");
        }
        if (this.resultType == ValueType.VOID) {
            throw new IllegalArgumentException("Compiled expression result type can't be void");
        }
        if (type != this.resultType) {
            throw new IllegalStateException("Result type expected is " + String.valueOf(type) + " but got " + String.valueOf(this.resultType));
        }
    }
    
    @Override
    public void pushOperand(@Nonnull final Parser.ParsedToken parsedToken) {
        // 
        // This method could not be decompiled.
        // 
        // Original Bytecode:
        // 
        //     1: getfield        com/hypixel/hytale/server/npc/util/expression/compile/CompileContext.operandStack:Ljava/util/Stack;
        //     4: aload_1         /* parsedToken */
        //     5: aload_0         /* this */
        //     6: invokestatic    invokestatic   !!! ERROR
        //     9: invokevirtual   java/util/Stack.push:(Ljava/lang/Object;)Ljava/lang/Object;
        //    12: pop            
        //    13: return         
        // 
        // The error that occurred was:
        // 
        // java.lang.ClassCastException
        // 
        throw new IllegalStateException("An error occurred while decompiling this method.");
    }
    
    @Override
    public void processOperator(@Nonnull final Parser.ParsedToken operator) throws ParseException {
        ASTOperator.fromParsedOperator(operator, this);
    }
    
    @Override
    public void processFunction(final int argumentCount) throws ParseException {
        ASTOperatorFunctionCall.fromParsedFunction(argumentCount, this);
    }
    
    @Override
    public void processTuple(@Nonnull final Parser.ParsedToken openingToken, final int argumentCount) {
        ASTOperatorTuple.fromParsedTuple(openingToken, argumentCount, this);
    }
    
    @Override
    public void done() {
        if (this.operandStack.size() != 1) {
            throw new IllegalStateException("Need exactly one returned value in expression");
        }
    }
}
