#include "decl.hpp"
#include "environ.hpp"
#include "ex.hpp"
#include "nodes.hpp"
#include "type.hpp"
#include "label.hpp"
#include "varnode.hpp"
#include "codegen.hpp"

#include <stdutil.hpp>
#include "declnode.hpp"

DeclSeqNode::DeclSeqNode() {}

DeclSeqNode::~DeclSeqNode()
{
	for (; decls.size(); decls.pop_back())
		delete decls.back();
}

//////////////////////////////
// Sequence of declarations //
//////////////////////////////
void DeclSeqNode::proto(DeclSeq* d, Environ* e)
{
	for (size_t k = 0; k < decls.size(); ++k) {
		try {
			decls[k]->proto(d, e);
		} catch (BlitzException& x) {
			if (x.pos < 0)
				x.pos = decls[k]->pos;
			if (!x.file.size())
				x.file = decls[k]->file;
			throw;
		}
	}
}

void DeclSeqNode::semant(Environ* e)
{
	for (size_t k = 0; k < decls.size(); ++k) {
		try {
			decls[k]->semant(e);
		} catch (BlitzException& x) {
			if (x.pos < 0)
				x.pos = decls[k]->pos;
			if (!x.file.size())
				x.file = decls[k]->file;
			throw;
		}
	}
}

void DeclSeqNode::translate(Codegen* g)
{
	for (size_t k = 0; k < decls.size(); ++k) {
		try {
			decls[k]->translate(g);
		} catch (BlitzException& x) {
			if (x.pos < 0)
				x.pos = decls[k]->pos;
			if (!x.file.size())
				x.file = decls[k]->file;
			throw;
		}
	}
}

void DeclSeqNode::transdata(Codegen* g)
{
	for (size_t k = 0; k < decls.size(); ++k) {
		try {
			decls[k]->transdata(g);
		} catch (BlitzException& x) {
			if (x.pos < 0)
				x.pos = decls[k]->pos;
			if (!x.file.size())
				x.file = decls[k]->file;
			throw;
		}
	}
}

void DeclSeqNode::push_back(DeclNode* d)
{
	decls.push_back(d);
}

int DeclSeqNode::size()
{
	return decls.size();
}

VarDeclNode::VarDeclNode(const std::string& i, const std::string& t, int k, bool c, ExprNode* e)
	: ident(i), tag(t), kind(k), constant(c), expr(e), sem_var(0)
{}

VarDeclNode::~VarDeclNode()
{
	delete expr;
	delete sem_var;
}

////////////////////////////
// Simple var declaration //
////////////////////////////
void VarDeclNode::proto(DeclSeq* d, Environ* e)
{
	Type* ty = tagType(tag, e);
	if (!ty)
		ty = Type::int_type;
	ConstType* defType = 0;

	if (expr != nullptr) {
		expr->semant(e);
		if (expr == nullptr) {
			ex("Internal Error: expr turned into nullptr (1)");
		}
		expr = expr->castTo(ty, e);
		if (expr == nullptr) {
			ex("Internal Error: expr turned into nullptr (2)");
		}

		if (constant || (kind & DECL_PARAM)) {
			ConstNode* c = static_cast<ConstNode*>(expr); //->constNode();

			if (!c)
				ex("Expression must be constant");
			if (ty == Type::int_type)
				ty = new ConstType(c->intValue());
			else if (ty == Type::float_type)
				ty = new ConstType(c->floatValue());
			else
				ty = new ConstType(c->stringValue());
			e->types.push_back(ty);
			delete expr;
			expr = 0;
		}

		if (expr == nullptr) {
			ex("Internal Error: expr turned into nullptr (3)");
		}

		if (kind & DECL_PARAM) { // Heap corruption, this being replaced again?
			defType = ty->constType();
			ty      = defType->valueType;
		}
	} else if (constant)
		ex("Constants must be initialized");

	Decl* decl = d->insertDecl(ident, ty, kind, defType);
	if (!decl)
		ex("Duplicate variable name");
	if (expr)
		sem_var = new DeclVarNode(decl);
}

void VarDeclNode::semant(Environ* e) {}

void VarDeclNode::translate(Codegen* g)
{
	if (kind & DECL_GLOBAL) {
		g->align_data(4);
		g->i_data(0, "_v" + ident);
	}
	if (expr)
		g->code(sem_var->store(g, expr->translate(g)));
}

FuncDeclNode::FuncDeclNode(const std::string& i, const std::string& t, DeclSeqNode* p, StmtSeqNode* ss)
	: ident(i), tag(t), params(p), stmts(ss)
{}

FuncDeclNode::~FuncDeclNode()
{
	delete params;
	delete stmts;
}

//////////////////////////
// Function Declaration //
//////////////////////////
void FuncDeclNode::proto(DeclSeq* d, Environ* e)
{
	Type* t = tagType(tag, e);
	if (!t)
		t = Type::int_type;
	a_ptr<DeclSeq> decls(new DeclSeq());
	params->proto(decls, e);
	sem_type = new FuncType(t, decls.release(), false, false);
	if (!d->insertDecl(ident, sem_type, DECL_FUNC)) {
		delete sem_type;
		ex("duplicate identifier");
	}
	e->types.push_back(sem_type);
}

void FuncDeclNode::semant(Environ* e)
{
	sem_env        = new Environ(genLabel(), sem_type->returnType, 1, e);
	DeclSeq* decls = sem_env->decls;

	int k;
	for (k = 0; k < sem_type->params->size(); ++k) {
		Decl* d = sem_type->params->decls[k];
		if (!decls->insertDecl(d->name, d->type, d->kind))
			ex("duplicate identifier");
	}

	stmts->semant(sem_env);
}

void FuncDeclNode::translate(Codegen* g)
{
	//var offsets
	int size = enumVars(sem_env);

	//enter function
	g->enter("_f" + ident, size);

	//initialize locals
	TNode* t = createVars(sem_env);
	if (t)
		g->code(t);
	if (g->debug) {
		std::string t = genLabel();
		g->s_data(ident, t);
		g->code(call("__bbDebugEnter", local(0), iconst((int)sem_env), global(t)));
	}

	//translate statements
	stmts->translate(g);

	for (size_t k = 0; k < sem_env->labels.size(); ++k) {
		if (sem_env->labels[k]->def < 0)
			ex("Undefined label", sem_env->labels[k]->ref);
	}

	//leave the function
	g->label(sem_env->funcLabel + "_leave");
	t = deleteVars(sem_env);
	if (g->debug)
		t = new TNode(IR_SEQ, call("__bbDebugLeave"), t);
	g->leave(t, sem_type->params->size() * 4);
}

StructDeclNode::StructDeclNode(const std::string& i, DeclSeqNode* f) : ident(i), fields(f) {}

StructDeclNode::~StructDeclNode()
{
	delete fields;
}

//////////////////////
// Type Declaration //
//////////////////////
void StructDeclNode::proto(DeclSeq* d, Environ* e)
{
	sem_type = new StructType(ident, new DeclSeq());
	if (!d->insertDecl(ident, sem_type, DECL_STRUCT)) {
		delete sem_type;
		ex("Duplicate identifier");
	}
	e->types.push_back(sem_type);
}

void StructDeclNode::semant(Environ* e)
{
	fields->proto(sem_type->fields, e);
	for (int k = 0; k < sem_type->fields->size(); ++k)
		sem_type->fields->decls[k]->offset = k * 4;
}

void StructDeclNode::translate(Codegen* g)
{
	//translate fields
	fields->translate(g);

	//type ID
	g->align_data(4);
	g->i_data(5, "_t" + ident);

	//used and free lists for type
	int k;
	for (k = 0; k < 2; ++k) {
		std::string lab = genLabel();
		g->i_data(0, lab); //fields
		g->p_data(lab);    //next
		g->p_data(lab);    //prev
		g->i_data(0);      //type
		g->i_data(-1);     //ref_cnt
	}

	//number of fields
	g->i_data(sem_type->fields->size());

	//type of each field
	for (k = 0; k < sem_type->fields->size(); ++k) {
		Decl*       field = sem_type->fields->decls[k];
		Type*       type  = field->type;
		std::string t;
		if (type == Type::int_type)
			t = "__bbIntType";
		else if (type == Type::float_type)
			t = "__bbFltType";
		else if (type == Type::string_type)
			t = "__bbStrType";
		else if (StructType* s = type->structType())
			t = "_t" + s->ident;
		else if (VectorType* v = type->vectorType())
			t = v->label;
		g->p_data(t);
	}
}

DataDeclNode::DataDeclNode(ExprNode* e) : expr(e) {}

DataDeclNode::~DataDeclNode()
{
	delete expr;
}

//////////////////////
// Data declaration //
//////////////////////
void DataDeclNode::proto(DeclSeq* d, Environ* e)
{
	expr         = expr->semant(e);
	ConstNode* c = expr->constNode();
	if (!c)
		ex("Data expression must be constant");
	if (expr->sem_type == Type::string_type)
		str_label = genLabel();
}

void DataDeclNode::semant(Environ* e) {}

void DataDeclNode::translate(Codegen* g)
{
	if (expr->sem_type != Type::string_type)
		return;
	ConstNode* c = expr->constNode();
	g->s_data(c->stringValue(), str_label);
}

void DataDeclNode::transdata(Codegen* g)
{
	ConstNode* c = expr->constNode();
	if (expr->sem_type == Type::int_type) {
		g->i_data(1);
		g->i_data(c->intValue());
	} else if (expr->sem_type == Type::float_type) {
		float n = c->floatValue();
		g->i_data(2);
		g->i_data(*(int*)&n);
	} else {
		g->i_data(4);
		g->p_data(str_label);
	}
}

VectorDeclNode::VectorDeclNode(const std::string& i, const std::string& t, ExprSeqNode* e, int k)
	: ident(i), tag(t), exprs(e), kind(k)
{}

VectorDeclNode::~VectorDeclNode()
{
	delete exprs;
}

////////////////////////
// Vector declaration //
////////////////////////
void VectorDeclNode::proto(DeclSeq* d, Environ* env)
{
	Type* ty = tagType(tag, env);
	if (!ty)
		ty = Type::int_type;

	std::vector<int> sizes;
	for (int k = 0; k < exprs->size(); ++k) {
		ExprNode*  e = exprs->exprs[k] = exprs->exprs[k]->semant(env);
		ConstNode* c                   = e->constNode();
		if (!c)
			ex("Blitz array sizes must be constant");
		int n = c->intValue();
		if (n < 0)
			ex("Blitz array sizes must not be negative");
		sizes.push_back(n + 1);
	}
	std::string label = genLabel();
	sem_type          = new VectorType(label, ty, sizes);
	if (!d->insertDecl(ident, sem_type, kind)) {
		delete sem_type;
		ex("Duplicate identifier");
	}
	env->types.push_back(sem_type);
}

void VectorDeclNode::translate(Codegen* g)
{
	//type tag!
	g->align_data(4);
	VectorType* v = sem_type->vectorType();
	g->i_data(6, v->label);
	int sz = 1;
	for (size_t k = 0; k < v->sizes.size(); ++k)
		sz *= v->sizes[k];
	g->i_data(sz);
	std::string t;
	Type*       type = v->elementType;
	if (type == Type::int_type)
		t = "__bbIntType";
	else if (type == Type::float_type)
		t = "__bbFltType";
	else if (type == Type::string_type)
		t = "__bbStrType";
	else if (StructType* s = type->structType())
		t = "_t" + s->ident;
	else if (VectorType* v = type->vectorType())
		t = v->label;
	g->p_data(t);

	if (kind == DECL_GLOBAL)
		g->i_data(0, "_v" + ident);
}

DeclNode::DeclNode() : pos(-1) {}

void DeclNode::proto(DeclSeq* d, Environ* e) {}

void DeclNode::semant(Environ* e) {}

void DeclNode::translate(Codegen* g) {}

void DeclNode::transdata(Codegen* g) {}
