#include "exprnode.hpp"
#include <cfloat>
#include <cmath>
#include "codegen.hpp"
#include "environ.hpp"
#include "toker.hpp"
#include "varnode.hpp"

#include <stdutil.hpp>

ExprNode::ExprNode() : sem_type(0) {}

ExprNode::ExprNode(Type* t) : sem_type(t) {}

//////////////////////////////////
// Cast an expression to a type //
//////////////////////////////////
ExprNode* ExprNode::castTo(Type* ty, Environ* e)
{
	if (!sem_type->canCastTo(ty)) {
		ex("Illegal type conversion");
	}

	ExprNode* cast = new CastNode(this, ty);
	cast->semant(e);
	return cast;
}

CastNode::CastNode(ExprNode* ex, Type* ty) : expr(ex), type(ty) {}

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

ExprNode* CastNode::semant(Environ* e)
{
	if (!expr->sem_type) {
		expr = expr->semant(e);
	}

	if (ConstNode* c = expr->constNode()) {
		ExprNode* e;
		if (type == Type::int_type)
			e = new IntConstNode(c->intValue());
		else if (type == Type::float_type)
			e = new FloatConstNode(c->floatValue());
		else
			e = new StringConstNode(c->stringValue());
		delete this;
		return e;
	}

	sem_type = type;
	return this;
}

//////////////////////////////////
// Cast an expression to a type //
//////////////////////////////////
TNode* CastNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	if (expr->sem_type == Type::float_type && sem_type == Type::int_type) {
		//float->int
		return new TNode(IR_CAST, t, 0);
	}
	if (expr->sem_type == Type::int_type && sem_type == Type::float_type) {
		//int->float
		return new TNode(IR_FCAST, t, 0);
	}
	if (expr->sem_type == Type::string_type && sem_type == Type::int_type) {
		//str->int
		return call("__bbStrToInt", t);
	}
	if (expr->sem_type == Type::int_type && sem_type == Type::string_type) {
		//int->str
		return call("__bbStrFromInt", t);
	}
	if (expr->sem_type == Type::string_type && sem_type == Type::float_type) {
		//str->float
		return fcall("__bbStrToFloat", t);
	}
	if (expr->sem_type == Type::float_type && sem_type == Type::string_type) {
		//float->str
		return call("__bbStrFromFloat", t);
	}
	if (expr->sem_type->structType() && sem_type == Type::string_type) {
		//obj->str
		return call("__bbObjToStr", t);
	}
	return t;
}

/////////////////////////////
// Sequence of Expressions //
/////////////////////////////
ExprSeqNode::~ExprSeqNode() {}

void ExprSeqNode::push_back(std::shared_ptr<ExprNode> e)
{
	exprs.push_back(e);
}

int ExprSeqNode::size()
{
	return exprs.size();
}

void ExprSeqNode::semant(Environ* e)
{
	for (int k = 0; k < exprs.size(); ++k) {
		if (exprs[k])
			exprs[k] = exprs[k]->semant(e);
	}
}

TNode* ExprSeqNode::translate(Codegen* g, bool cfunc)
{
	TNode *t = 0, *l = 0;
	for (int k = 0; k < exprs.size(); ++k) {
		TNode* q = exprs[k]->translate(g);

		if (cfunc) {
			Type* ty = exprs[k]->sem_type;
			if (ty->stringType()) {
				q = call("__bbStrToCStr", q);
			} else if (ty->structType()) {
				q = new TNode(IR_MEM, q);
			} else if (ty == Type::void_type) {
				q = new TNode(IR_MEM, add(q, iconst(4)));
			}
		}

		TNode* p;
		p = new TNode(IR_ARG, 0, 0, k * 4);
		p = new TNode(IR_MEM, p, 0);
		p = new TNode(IR_MOVE, q, p);
		p = new TNode(IR_SEQ, p, 0);
		if (l)
			l->r = p;
		else
			t = p;
		l = p;
	}
	return t;
}

void ExprSeqNode::castTo(DeclSeq* decls, Environ* e, bool cfunc)
{
	if (exprs.size() > decls->size())
		ex("Too many parameters");
	for (int k = 0; k < decls->size(); ++k) {
		Decl* d = decls->decls[k];
		if (k < exprs.size() && exprs[k]) {
			if (cfunc && d->type->structType()) {
				if (exprs[k]->sem_type->structType()) {
				} else if (exprs[k]->sem_type->intType()) {
					exprs[k]->sem_type = Type::void_type;
				} else {
					ex("Illegal type conversion");
				}
				continue;
			}

			exprs[k] = exprs[k]->castTo(d->type, e);

		} else {
			if (!d->defType)
				ex("Not enough parameters");
			ExprNode* expr = constValue(d->defType);
			if (k < exprs.size())
				exprs[k] = expr;
			else
				exprs.push_back(expr);
		}
	}
}

void ExprSeqNode::castTo(Type* t, Environ* e)
{
	for (int k = 0; k < exprs.size(); ++k) {
		exprs[k] = exprs[k]->castTo(t, e);
	}
}

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

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

///////////////////
// Function call //
///////////////////
ExprNode* CallNode::semant(Environ* e)
{
	Type* t  = e->findType(tag);
	sem_decl = e->findFunc(ident);
	if (!sem_decl || !(sem_decl->kind & DECL_FUNC))
		ex("Function '" + ident + "' not found");
	FuncType* f = sem_decl->type->funcType();
	if (t && f->returnType != t)
		ex("incorrect function return type");
	exprs->semant(e);
	exprs->castTo(f->params, e, f->cfunc);
	sem_type = f->returnType;
	return this;
}

TNode* CallNode::translate(Codegen* g)
{
	FuncType* f = sem_decl->type->funcType();

	TNode* t;
	TNode* l = global("_f" + ident);
	TNode* r = exprs->translate(g, f->cfunc);

	if (f->userlib) {
		l = new TNode(IR_MEM, l);
		usedfuncs.insert(ident);
	}

	if (sem_type == Type::float_type) {
		t = new TNode(IR_FCALL, l, r, exprs->size() * 4);
	} else {
		t = new TNode(IR_CALL, l, r, exprs->size() * 4);
	}

	if (f->returnType->stringType()) {
		if (f->cfunc) {
			t = call("__bbCStrToStr", t);
		}
	}
	return t;
}

VarExprNode::VarExprNode(VarNode* v) : var(v) {}

VarExprNode::~VarExprNode()
{
	delete var;
}

/////////////////////////
// Variable expression //
/////////////////////////
ExprNode* VarExprNode::semant(Environ* e)
{
	var->semant(e);
	sem_type     = var->sem_type;
	ConstType* c = sem_type->constType();
	if (!c)
		return this;
	ExprNode* expr = constValue(c);
	delete this;
	return expr;
}

TNode* VarExprNode::translate(Codegen* g)
{
	return var->load(g);
}

//////////////////////
// Integer constant //
//////////////////////
IntConstNode::IntConstNode(int n) : value(n)
{
	sem_type = Type::int_type;
}

TNode* IntConstNode::translate(Codegen* g)
{
	return new TNode(IR_CONST, 0, 0, value);
}

int IntConstNode::intValue()
{
	return value;
}

float IntConstNode::floatValue()
{
	return value;
}

std::string IntConstNode::stringValue()
{
	return itoa(value);
}

////////////////////
// Float constant //
////////////////////
FloatConstNode::FloatConstNode(float f) : value(f)
{
	sem_type = Type::float_type;
}

TNode* FloatConstNode::translate(Codegen* g)
{
	return new TNode(IR_CONST, 0, 0, *(int*)&value);
}

int FloatConstNode::intValue()
{
	float flt = value;
	int   temp;
	_control87(_RC_NEAR | _PC_24 | _EM_INVALID | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT
				   | _EM_DENORMAL,
			   0xfffff);
	_asm {
		fld[flt];
		fistp[temp];
	}
	_control87(_CW_DEFAULT, 0xfffff);
	return temp;
}

float FloatConstNode::floatValue()
{
	return value;
}

std::string FloatConstNode::stringValue()
{
	return ftoa(value);
}

/////////////////////
// String constant //
/////////////////////
StringConstNode::StringConstNode(const std::string& s) : value(s)
{
	sem_type = Type::string_type;
}

TNode* StringConstNode::translate(Codegen* g)
{
	std::string lab = genLabel();
	g->s_data(value, lab);
	return call("__bbStrConst", global(lab));
}

int StringConstNode::intValue()
{
	return atoi(value);
}

float StringConstNode::floatValue()
{
	return (float)atof(value);
}

std::string StringConstNode::stringValue()
{
	return value;
}

UniExprNode::UniExprNode(int op, ExprNode* expr) : op(op), expr(expr) {}

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

////////////////////
// Unary operator //
////////////////////
ExprNode* UniExprNode::semant(Environ* e)
{
	expr     = expr->semant(e);
	sem_type = expr->sem_type;
	if (sem_type != Type::int_type && sem_type != Type::float_type)
		ex("Illegal operator for type");
	if (ConstNode* c = expr->constNode()) {
		ExprNode* e;
		if (sem_type == Type::int_type) {
			switch (op) {
			case '+':
				e = new IntConstNode(+c->intValue());
				break;
			case '-':
				e = new IntConstNode(-c->intValue());
				break;
			case ABS:
				e = new IntConstNode(c->intValue() >= 0 ? c->intValue() : -c->intValue());
				break;
			case SGN:
				e = new IntConstNode(c->intValue() > 0 ? 1 : (c->intValue() < 0 ? -1 : 0));
				break;
			}
		} else {
			switch (op) {
			case '+':
				e = new FloatConstNode(+c->floatValue());
				break;
			case '-':
				e = new FloatConstNode(-c->floatValue());
				break;
			case ABS:
				e = new FloatConstNode(c->floatValue() >= 0 ? c->floatValue() : -c->floatValue());
				break;
			case SGN:
				e = new FloatConstNode(c->floatValue() > 0 ? 1 : (c->floatValue() < 0 ? -1 : 0));
				break;
			}
		}
		delete this;
		return e = nullptr;
	}
	return this;
}

TNode* UniExprNode::translate(Codegen* g)
{
	int    n = 0;
	TNode* l = expr->translate(g);
	if (sem_type == Type::int_type) {
		switch (op) {
		case '+':
			return l;
		case '-':
			n = IR_NEG;
			break;
		case ABS:
			return call("__bbAbs", l);
		case SGN:
			return call("__bbSgn", l);
		}
	} else {
		switch (op) {
		case '+':
			return l;
		case '-':
			n = IR_FNEG;
			break;
		case ABS:
			return fcall("__bbFAbs", l);
		case SGN:
			return fcall("__bbFSgn", l);
		}
	}
	return new TNode(n, l, 0);
}

BinExprNode::BinExprNode(int op, ExprNode* lhs, ExprNode* rhs) : op(op), lhs(lhs), rhs(rhs) {}

BinExprNode::~BinExprNode()
{
	delete lhs;
	delete rhs;
}

/////////////////////////////////////////////////////
// boolean expression - accepts ints, returns ints //
/////////////////////////////////////////////////////
ExprNode* BinExprNode::semant(Environ* e)
{
	lhs           = lhs->semant(e);
	lhs           = lhs->castTo(Type::int_type, e);
	rhs           = rhs->semant(e);
	rhs           = rhs->castTo(Type::int_type, e);
	ConstNode *lc = lhs->constNode(), *rc = rhs->constNode();
	if (lc && rc) {
		ExprNode* expr = nullptr;
		switch (op) {
		case AND:
			expr = new IntConstNode(lc->intValue() & rc->intValue());
			break;
		case OR:
			expr = new IntConstNode(lc->intValue() | rc->intValue());
			break;
		case XOR:
			expr = new IntConstNode(lc->intValue() ^ rc->intValue());
			break;
		case SHL:
			expr = new IntConstNode(lc->intValue() << rc->intValue());
			break;
		case SHR:
			expr = new IntConstNode((unsigned)lc->intValue() >> rc->intValue());
			break;
		case SAR:
			expr = new IntConstNode(lc->intValue() >> rc->intValue());
			break;
		}
		delete this;
		return expr;
	}
	sem_type = Type::int_type;
	return this;
}

TNode* BinExprNode::translate(Codegen* g)
{
	TNode* l = lhs->translate(g);
	TNode* r = rhs->translate(g);
	int    n = 0;
	switch (op) {
	case AND:
		n = IR_AND;
		break;
	case OR:
		n = IR_OR;
		break;
	case XOR:
		n = IR_XOR;
		break;
	case SHL:
		n = IR_SHL;
		break;
	case SHR:
		n = IR_SHR;
		break;
	case SAR:
		n = IR_SAR;
		break;
	}
	return new TNode(n, l, r);
}

ArithExprNode::ArithExprNode(int op, ExprNode* lhs, ExprNode* rhs) : op(op), lhs(lhs), rhs(rhs) {}

ArithExprNode::~ArithExprNode()
{
	delete lhs;
	delete rhs;
}

///////////////////////////
// arithmetic expression //
///////////////////////////
ExprNode* ArithExprNode::semant(Environ* e)
{
	lhs = lhs->semant(e);
	rhs = rhs->semant(e);
	if (lhs->sem_type->structType() || rhs->sem_type->structType()) {
		ex("Arithmetic operator cannot be applied to custom type objects");
	}
	if (lhs->sem_type == Type::string_type || rhs->sem_type == Type::string_type) {
		//one side is a string - only + operator...
		if (op != '+')
			ex("Operator cannot be applied to strings");
		sem_type = Type::string_type;
	} else if (op == '^' || lhs->sem_type == Type::float_type || rhs->sem_type == Type::float_type) {
		//It's ^, or one side is a float
		sem_type = Type::float_type;
	} else {
		//must be 2 ints
		sem_type = Type::int_type;
	}
	lhs           = lhs->castTo(sem_type, e);
	rhs           = rhs->castTo(sem_type, e);
	ConstNode *lc = lhs->constNode(), *rc = rhs->constNode();
	if (rc && (op == '/' || op == MOD)) {
		if ((sem_type == Type::int_type && !rc->intValue()) || (sem_type == Type::float_type && !rc->floatValue())) {
			ex("Division by zero");
		}
	}
	if (lc && rc) {
		ExprNode* expr = nullptr;
		if (sem_type == Type::string_type) {
			expr = new StringConstNode(lc->stringValue() + rc->stringValue());
		} else if (sem_type == Type::int_type) {
			switch (op) {
			case '+':
				expr = new IntConstNode(lc->intValue() + rc->intValue());
				break;
			case '-':
				expr = new IntConstNode(lc->intValue() - rc->intValue());
				break;
			case '*':
				expr = new IntConstNode(lc->intValue() * rc->intValue());
				break;
			case '/':
				expr = new IntConstNode(lc->intValue() / rc->intValue());
				break;
			case MOD:
				expr = new IntConstNode(lc->intValue() % rc->intValue());
				break;
			}
		} else {
			switch (op) {
			case '+':
				expr = new FloatConstNode(lc->floatValue() + rc->floatValue());
				break;
			case '-':
				expr = new FloatConstNode(lc->floatValue() - rc->floatValue());
				break;
			case '*':
				expr = new FloatConstNode(lc->floatValue() * rc->floatValue());
				break;
			case '/':
				expr = new FloatConstNode(lc->floatValue() / rc->floatValue());
				break;
			case MOD:
				expr = new FloatConstNode(fmod(lc->floatValue(), rc->floatValue()));
				break;
			case '^':
				expr = new FloatConstNode(pow(lc->floatValue(), rc->floatValue()));
				break;
			}
		}
		delete this;
		return expr;
	}
	return this;
}

TNode* ArithExprNode::translate(Codegen* g)
{
	TNode* l = lhs->translate(g);
	TNode* r = rhs->translate(g);
	if (sem_type == Type::string_type) {
		return call("__bbStrConcat", l, r);
	}
	int n = 0;
	if (sem_type == Type::int_type) {
		switch (op) {
		case '+':
			n = IR_ADD;
			break;
		case '-':
			n = IR_SUB;
			break;
		case '*':
			n = IR_MUL;
			break;
		case '/':
			n = IR_DIV;
			break;
		case MOD:
			return call("__bbMod", l, r);
		}
	} else {
		switch (op) {
		case '+':
			n = IR_FADD;
			break;
		case '-':
			n = IR_FSUB;
			break;
		case '*':
			n = IR_FMUL;
			break;
		case '/':
			n = IR_FDIV;
			break;
		case MOD:
			return fcall("__bbFMod", l, r);
		case '^':
			return fcall("__bbFPow", l, r);
		}
	}
	return new TNode(n, l, r);
}

RelExprNode::RelExprNode(int op, ExprNode* lhs, ExprNode* rhs) : op(op), lhs(lhs), rhs(rhs) {}

RelExprNode::~RelExprNode()
{
	delete lhs;
	delete rhs;
}

/////////////////////////
// relation expression //
/////////////////////////
ExprNode* RelExprNode::semant(Environ* e)
{
	lhs = lhs->semant(e);
	rhs = rhs->semant(e);
	if (lhs->sem_type->structType() || rhs->sem_type->structType()) {
		if (op != '=' && op != NE)
			ex("Illegal operator for custom type objects");
		opType = lhs->sem_type != Type::null_type ? lhs->sem_type : rhs->sem_type;
	} else if (lhs->sem_type == Type::string_type || rhs->sem_type == Type::string_type) {
		opType = Type::string_type;
	} else if (lhs->sem_type == Type::float_type || rhs->sem_type == Type::float_type) {
		opType = Type::float_type;
	} else {
		opType = Type::int_type;
	}
	sem_type      = Type::int_type;
	lhs           = lhs->castTo(opType, e);
	rhs           = rhs->castTo(opType, e);
	ConstNode *lc = lhs->constNode(), *rc = rhs->constNode();
	if (lc && rc) {
		ExprNode* expr = nullptr;
		if (opType == Type::string_type) {
			switch (op) {
			case '<':
				expr = new IntConstNode(lc->stringValue() < rc->stringValue());
				break;
			case '=':
				expr = new IntConstNode(lc->stringValue() == rc->stringValue());
				break;
			case '>':
				expr = new IntConstNode(lc->stringValue() > rc->stringValue());
				break;
			case LE:
				expr = new IntConstNode(lc->stringValue() <= rc->stringValue());
				break;
			case NE:
				expr = new IntConstNode(lc->stringValue() != rc->stringValue());
				break;
			case GE:
				expr = new IntConstNode(lc->stringValue() >= rc->stringValue());
				break;
			}
		} else if (opType == Type::float_type) {
			switch (op) {
			case '<':
				expr = new IntConstNode(lc->floatValue() < rc->floatValue());
				break;
			case '=':
				expr = new IntConstNode(lc->floatValue() == rc->floatValue());
				break;
			case '>':
				expr = new IntConstNode(lc->floatValue() > rc->floatValue());
				break;
			case LE:
				expr = new IntConstNode(lc->floatValue() <= rc->floatValue());
				break;
			case NE:
				expr = new IntConstNode(lc->floatValue() != rc->floatValue());
				break;
			case GE:
				expr = new IntConstNode(lc->floatValue() >= rc->floatValue());
				break;
			}
		} else {
			switch (op) {
			case '<':
				expr = new IntConstNode(lc->intValue() < rc->intValue());
				break;
			case '=':
				expr = new IntConstNode(lc->intValue() == rc->intValue());
				break;
			case '>':
				expr = new IntConstNode(lc->intValue() > rc->intValue());
				break;
			case LE:
				expr = new IntConstNode(lc->intValue() <= rc->intValue());
				break;
			case NE:
				expr = new IntConstNode(lc->intValue() != rc->intValue());
				break;
			case GE:
				expr = new IntConstNode(lc->intValue() >= rc->intValue());
				break;
			}
		}
		delete this;
		return expr;
	}
	return this;
}

TNode* RelExprNode::translate(Codegen* g)
{
	TNode* l = lhs->translate(g);
	TNode* r = rhs->translate(g);
	return compare(op, l, r, opType);
}

NewNode::NewNode(const std::string& i) : ident(i) {}

////////////////////
// New expression //
////////////////////
ExprNode* NewNode::semant(Environ* e)
{
	sem_type = e->findType(ident);
	if (!sem_type)
		ex("custom type name not found");
	if (sem_type->structType() == 0)
		ex("type is not a custom type");
	return this;
}

TNode* NewNode::translate(Codegen* g)
{
	return call("__bbObjNew", global("_t" + ident));
}

FirstNode::FirstNode(const std::string& i) : ident(i) {}

////////////////////
// First of class //
////////////////////
ExprNode* FirstNode::semant(Environ* e)
{
	sem_type = e->findType(ident);
	if (!sem_type)
		ex("custom type name name not found");
	return this;
}

TNode* FirstNode::translate(Codegen* g)
{
	return call("__bbObjFirst", global("_t" + ident));
}

LastNode::LastNode(const std::string& i) : ident(i) {}

///////////////////
// Last of class //
///////////////////
ExprNode* LastNode::semant(Environ* e)
{
	sem_type = e->findType(ident);
	if (!sem_type)
		ex("custom type name not found");
	return this;
}

TNode* LastNode::translate(Codegen* g)
{
	return call("__bbObjLast", global("_t" + ident));
}

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

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

////////////////////
// Next of object //
////////////////////
ExprNode* AfterNode::semant(Environ* e)
{
	expr = expr->semant(e);
	if (expr->sem_type == Type::null_type)
		ex("'After' cannot be used on 'Null'");
	if (expr->sem_type->structType() == 0)
		ex("'After' must be used with a custom type object");
	sem_type = expr->sem_type;
	return this;
}

TNode* AfterNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	if (g->debug)
		t = jumpf(t, "__bbNullObjEx");
	return call("__bbObjNext", t);
}

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

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

////////////////////
// Prev of object //
////////////////////
ExprNode* BeforeNode::semant(Environ* e)
{
	expr = expr->semant(e);
	if (expr->sem_type == Type::null_type)
		ex("'Before' cannot be used with 'Null'");
	if (expr->sem_type->structType() == 0)
		ex("'Before' must be used with a custom type object");
	sem_type = expr->sem_type;
	return this;
}

TNode* BeforeNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	if (g->debug)
		t = jumpf(t, "__bbNullObjEx");
	return call("__bbObjPrev", t);
}

/////////////////
// Null object //
/////////////////
ExprNode* NullNode::semant(Environ* e)
{
	sem_type = Type::null_type;
	return this;
}

TNode* NullNode::translate(Codegen* g)
{
	return new TNode(IR_CONST, 0, 0, 0);
}

ObjectCastNode::ObjectCastNode(ExprNode* e, const std::string& t) : expr(e), type_ident(t) {}

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

/////////////////
// Object cast //
/////////////////
ExprNode* ObjectCastNode::semant(Environ* e)
{
	expr     = expr->semant(e);
	expr     = expr->castTo(Type::int_type, e);
	sem_type = e->findType(type_ident);
	if (!sem_type)
		ex("custom type name not found");
	if (!sem_type->structType())
		ex("type is not a custom type");
	return this;
}

TNode* ObjectCastNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	t        = call("__bbObjFromHandle", t, global("_t" + sem_type->structType()->ident));
	return t;
}

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

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

///////////////////
// Object Handle //
///////////////////
ExprNode* ObjectHandleNode::semant(Environ* e)
{
	expr = expr->semant(e);
	if (!expr->sem_type->structType())
		ex("'ObjectHandle' must be used with an object");
	sem_type = Type::int_type;
	return this;
}

TNode* ObjectHandleNode::translate(Codegen* g)
{
	TNode* t = expr->translate(g);
	return call("__bbObjToHandle", t);
}

ExprNode* ConstNode::semant(Environ* e)
{
	return this;
}

ConstNode* ConstNode::constNode()
{
	return this;
}
