#include "varnode.hpp"
#include "codegen.hpp"
#include "environ.hpp"
#include "exprnode.hpp"
#include "type.hpp"

//////////////////////////////////
// Common get/set for variables //
//////////////////////////////////
TNode* VarNode::load(Codegen* g)
{
	TNode* t = translate(g);
	if (sem_type == Type::string_type)
		return call("__bbStrLoad", t);
	return mem(t);
}

TNode* VarNode::store(Codegen* g, TNode* n)
{
	TNode* t = translate(g);
	if (sem_type->structType())
		return call("__bbObjStore", t, n);
	if (sem_type == Type::string_type)
		return call("__bbStrStore", t, n);
	return move(n, mem(t));
}

bool VarNode::isObjParam()
{
	return false;
}

DeclVarNode::DeclVarNode(Decl* d) : sem_decl(d)
{
	if (d)
		sem_type = d->type;
}

//////////////////
// Declared var //
//////////////////
void DeclVarNode::semant(Environ* e) {}

TNode* DeclVarNode::translate(Codegen* g)
{
	if (sem_decl->kind == DECL_GLOBAL)
		return global("_v" + sem_decl->name);
	return local(sem_decl->offset);
}

TNode* DeclVarNode::store(Codegen* g, TNode* n)
{
	if (isObjParam()) {
		TNode* t = translate(g);
		return move(n, mem(t));
	}
	return VarNode::store(g, n);
}

bool DeclVarNode::isObjParam()
{
	return sem_type->structType() && sem_decl->kind == DECL_PARAM;
}

IdentVarNode::IdentVarNode(const std::string& i, const std::string& t) : ident(i), tag(t) {}

///////////////
// Ident var //
///////////////
void IdentVarNode::semant(Environ* e)
{
	if (sem_decl)
		return;
	Type* t = tagType(tag, e);
	if (!t)
		t = Type::int_type;
	if (sem_decl = e->findDecl(ident)) {
		if (!(sem_decl->kind & (DECL_GLOBAL | DECL_LOCAL | DECL_PARAM))) {
			ex("Identifier '" + sem_decl->name + "' may not be used like this");
		}
		Type* ty = sem_decl->type;
		if (ty->constType())
			ty = ty->constType()->valueType;
		if (tag.size() && t != ty)
			ex("Variable type mismatch");
	} else {
		//ugly auto decl!
		sem_decl = e->decls->insertDecl(ident, t, DECL_LOCAL);
	}
	sem_type = sem_decl->type;
}

/////////////////
// Indexed Var //
/////////////////
void ArrayVarNode::semant(Environ* e)
{
	exprs->semant(e);
	exprs->castTo(Type::int_type, e);
	Type* t  = e->findType(tag);
	sem_decl = e->findDecl(ident);
	if (!sem_decl || !(sem_decl->kind & DECL_ARRAY))
		ex("Array not found");
	ArrayType* a = sem_decl->type->arrayType();
	if (t && t != a->type)
		ex("array type mismtach");
	if (a->size != exprs->size())
		ex("incorrect number of dimensions");
	sem_type = a->type;
}

TNode* ArrayVarNode::translate(Codegen* g)
{
	TNode* t = 0;
	for (int k = 0; k < exprs->size(); ++k) {
		TNode* e = exprs->exprs[k]->translate(g);
		if (k) {
			TNode* s = mem(add(global("_a" + ident), iconst(k * 4 + 8)));
			e        = add(t, mul(e, s));
		}
		if (g->debug) {
			TNode* s = mem(add(global("_a" + ident), iconst(k * 4 + 12)));
			t        = jumpge(e, s, "__bbArrayBoundsEx");
		} else
			t = e;
	}
	t = add(mem(global("_a" + ident)), mul(t, iconst(4)));
	return t;
}

///////////////
// Field var //
///////////////
void FieldVarNode::semant(Environ* e)
{
	expr          = expr->semant(e);
	StructType* s = expr->sem_type->structType();
	if (!s)
		ex("Variable must be a Type");
	sem_field = s->fields->findDecl(ident);
	if (!sem_field)
		ex("Type field not found");
	sem_type = sem_field->type;
}

TNode* FieldVarNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	if (g->debug)
		t = jumpf(t, "__bbNullObjEx");
	t = mem(t);
	if (g->debug)
		t = jumpf(t, "__bbNullObjEx");
	return add(t, iconst(sem_field->offset));
}

VectorVarNode::VectorVarNode(ExprNode* e, ExprSeqNode* es) : expr(e), exprs(es) {}

VectorVarNode::~VectorVarNode()
{
	delete expr;
	delete exprs;
}

////////////////
// Vector var //
////////////////
void VectorVarNode::semant(Environ* e)
{
	expr     = expr->semant(e);
	vec_type = expr->sem_type->vectorType();
	if (!vec_type)
		ex("Variable must be a Blitz array");
	if (vec_type->sizes.size() != exprs->size())
		ex("Incorrect number of subscripts");
	exprs->semant(e);
	exprs->castTo(Type::int_type, e);
	for (int k = 0; k < exprs->size(); ++k) {
		if (ConstNode* t = exprs->exprs[k]->constNode()) {
			if (t->intValue() >= vec_type->sizes[k]) {
				ex("Blitz array subscript out of range");
			}
		}
	}
	sem_type = vec_type->elementType;
}

TNode* VectorVarNode::translate(Codegen* g)
{
	int    sz = 4;
	TNode* t  = 0;
	for (int k = 0; k < exprs->size(); ++k) {
		TNode*    p;
		ExprNode* e = exprs->exprs[k];
		if (ConstNode* t = e->constNode()) {
			p = iconst(t->intValue() * sz);
		} else {
			p = e->translate(g);
			if (g->debug) {
				p = jumpge(p, iconst(vec_type->sizes[k]), "__bbVecBoundsEx");
			}
			p = mul(p, iconst(sz));
		}
		sz = sz * vec_type->sizes[k];
		t  = t ? add(t, p) : p;
	}
	return add(t, expr->translate(g));
}

ArrayVarNode::ArrayVarNode(const std::string& i, const std::string& t, ExprSeqNode* e) : ident(i), tag(t), exprs(e) {}

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

FieldVarNode::FieldVarNode(ExprNode* e, const std::string& i, const std::string& t) : expr(e), ident(i), tag(t) {}

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