#include "toy_ast.h"

void Toy_private_initAstBlock(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_BLOCK;
	tmp->block.innerScope = false;
	tmp->block.child = NULL;
	tmp->block.next = NULL;
	tmp->block.tail = NULL;

	(*astHandle) = tmp;
}

void Toy_private_appendAstBlock(Toy_Bucket** bucketHandle, Toy_Ast* block, Toy_Ast* child) {
	//first, check if we're an empty head
	if (block->block.child == NULL) {
		block->block.child = child;
		return; //First call on an empty head skips any memory allocations
	}

	//run (or jump) until we hit the current tail
	Toy_Ast* iter = block->block.tail ? block->block.tail : block;

	while(iter->block.next != NULL) {
		iter = iter->block.next;
	}

	//append a new link to the chain
	Toy_private_initAstBlock(bucketHandle, &(iter->block.next));

	//store the child in the new link, prep the tail pointer
	iter->block.next->block.child = child;
	block->block.tail = iter->block.next;
}

void Toy_private_emitAstValue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Value value) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_VALUE;
	tmp->value.value = value;

	(*astHandle) = tmp;
}

void Toy_private_emitAstUnary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_UNARY;
	tmp->unary.flag = flag;
	tmp->unary.child = *astHandle;

	(*astHandle) = tmp;
}

void Toy_private_emitAstBinary(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_BINARY;
	tmp->binary.flag = flag;
	tmp->binary.left = *astHandle; //left-recursive
	tmp->binary.right = right;

	(*astHandle) = tmp;
}

void Toy_private_emitAstBinaryShortCircuit(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_BINARY_SHORT_CIRCUIT;
	tmp->binary.flag = flag;
	tmp->binary.left = *astHandle; //left-recursive
	tmp->binary.right = right;

	(*astHandle) = tmp;
}

void Toy_private_emitAstCompare(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_COMPARE;
	tmp->compare.flag = flag;
	tmp->compare.left = *astHandle; //left-recursive
	tmp->compare.right = right;

	(*astHandle) = tmp;
}

void Toy_private_emitAstGroup(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_GROUP;
	tmp->group.child = (*astHandle);

	(*astHandle) = tmp;
}

void Toy_private_emitAstCompound(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_COMPOUND;
	tmp->compound.flag = flag;
	tmp->compound.child = *astHandle;

	(*astHandle) = tmp;
}

void Toy_private_emitAstAggregate(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* right) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_AGGREGATE;
	tmp->aggregate.flag = flag;
	tmp->aggregate.left = *astHandle; //left-recursive
	tmp->aggregate.right = right;

	(*astHandle) = tmp;
}

void Toy_private_emitAstAssert(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* child, Toy_Ast* msg) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_ASSERT;
	tmp->assert.child = child;
	tmp->assert.message = msg;

	(*astHandle) = tmp;
}

void Toy_private_emitAstIfThenElse(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch, Toy_Ast* elseBranch) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_IF_THEN_ELSE;
	tmp->ifThenElse.condBranch = condBranch;
	tmp->ifThenElse.thenBranch = thenBranch;
	tmp->ifThenElse.elseBranch = elseBranch;

	(*astHandle) = tmp;
}

void Toy_private_emitAstWhileThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_WHILE_THEN;
	tmp->whileThen.condBranch = condBranch;
	tmp->whileThen.thenBranch = thenBranch;

	(*astHandle) = tmp;
}

void Toy_private_emitAstForThen(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* condBranch, Toy_Ast* thenBranch) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_FOR_THEN;
	tmp->forThen.condBranch = condBranch;
	tmp->forThen.thenBranch = thenBranch;

	(*astHandle) = tmp;
}

void Toy_private_emitAstBreak(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_BREAK;

	(*astHandle) = tmp;
}

void Toy_private_emitAstContinue(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_CONTINUE;

	(*astHandle) = tmp;
}

void Toy_private_emitAstReturn(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_RETURN;
	tmp->fnReturn.child = (*astHandle);

	(*astHandle) = tmp;
}

void Toy_private_emitAstPrint(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_PRINT;
	tmp->print.child = (*astHandle);

	(*astHandle) = tmp;
}

void Toy_private_emitAstVariableDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_ValueType valueType, bool constant, Toy_Ast* expr) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_VAR_DECLARE;
	tmp->varDeclare.name = name;
	tmp->varDeclare.valueType = valueType;
	tmp->varDeclare.constant = constant;
	tmp->varDeclare.expr = expr;

	(*astHandle) = tmp;
}

void Toy_private_emitAstVariableAssignment(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_AstFlag flag, Toy_Ast* expr) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_VAR_ASSIGN;
	tmp->varAssign.flag = flag;
	tmp->varAssign.target = (*astHandle);
	tmp->varAssign.expr = expr;

	(*astHandle) = tmp;
}

void Toy_private_emitAstVariableAccess(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_VAR_ACCESS;
	tmp->varAccess.child = (*astHandle);

	(*astHandle) = tmp;
}

void Toy_private_emitAstFunctionDeclaration(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_String* name, Toy_Ast* params, Toy_Ast* body) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_FN_DECLARE;
	tmp->fnDeclare.name = name;
	tmp->fnDeclare.params = params;
	tmp->fnDeclare.body = body;

	(*astHandle) = tmp;
}

void Toy_private_emitAstFunctionInvokation(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* args) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_FN_INVOKE;
	tmp->fnInvoke.function = (*astHandle);
	tmp->fnInvoke.args = args;

	(*astHandle) = tmp;
}

void Toy_private_emitAstAttribute(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_ATTRIBUTE;
	tmp->attribute.left = (*astHandle);
	tmp->attribute.right = expr;

	(*astHandle) = tmp;
}

void Toy_private_emitAstIterable(Toy_Bucket** bucketHandle, Toy_Ast** astHandle, Toy_Ast* expr) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_ITERABLE;
	tmp->iterable.left = (*astHandle);
	tmp->iterable.right = expr;

	(*astHandle) = tmp;
}

void Toy_private_emitAstStackPop(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_STACK_POP;
	tmp->stackPop.child = (*astHandle);

	(*astHandle) = tmp;
}

void Toy_private_emitAstPass(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_PASS;

	(*astHandle) = tmp;
}

void Toy_private_emitAstError(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_ERROR;

	(*astHandle) = tmp;
}

void Toy_private_emitAstEnd(Toy_Bucket** bucketHandle, Toy_Ast** astHandle) {
	Toy_Ast* tmp = (Toy_Ast*)Toy_partitionBucket(bucketHandle, sizeof(Toy_Ast));

	tmp->type = TOY_AST_END;

	(*astHandle) = tmp;
}

const char* Toy_private_getAstTypeAsCString(Toy_AstType type) {
	switch(type) {
		case TOY_AST_BLOCK: return "BLOCK";

		case TOY_AST_VALUE: return "VALUE";
		case TOY_AST_UNARY: return "UNARY";
		case TOY_AST_BINARY: return "BINARY";
		case TOY_AST_BINARY_SHORT_CIRCUIT: return "BINARY_SHORT_CIRCUIT";
		case TOY_AST_COMPARE: return "COMPARE";
		case TOY_AST_GROUP: return "GROUP";
		case TOY_AST_COMPOUND: return "COMPOUND";
		case TOY_AST_AGGREGATE: return "AGGREGATE";

		case TOY_AST_ASSERT: return "ASSERT";
		case TOY_AST_IF_THEN_ELSE: return "IF_THEN_ELSE";
		case TOY_AST_WHILE_THEN: return "WHILE_THEN";
		case TOY_AST_FOR_THEN: return "FOR_THEN";
		case TOY_AST_BREAK: return "BREAK";
		case TOY_AST_CONTINUE: return "CONTINUE";
		case TOY_AST_RETURN: return "RETURN";
		case TOY_AST_PRINT: return "PRINT";

		case TOY_AST_VAR_DECLARE: return "DECLARE";
		case TOY_AST_VAR_ASSIGN: return "ASSIGN";
		case TOY_AST_VAR_ACCESS: return "ACCESS";

		case TOY_AST_FN_DECLARE: return "FN_DECLARE";
		case TOY_AST_FN_INVOKE: return "FN_INVOKE";
		case TOY_AST_ATTRIBUTE: return "ATTRIBUTE";
		case TOY_AST_ITERABLE: return "ITERABLE";

		case TOY_AST_STACK_POP: return "STACK_POP";

		case TOY_AST_PASS: return "PASS";
		case TOY_AST_ERROR: return "ERROR";
		case TOY_AST_END: return "END";
	}

	return NULL;
}