// px_internal.cpp
// ```````````````

#include <cstdarg>

#define PX_INTERNAL_SOURCE
#include "px_internal.h"
#include "px_typechecker.h"

//
// PRETTY_PRINTER
//

const char* pexl_pprint_typename(enum tok_types t) {
	switch (t) {
		case TOK_BODY:				return "BODY"; 
		case TOK_BLOCK:				return "BLOCK"; 
		case TOK_STATEMENT:			return "STATEMENT"; 
		case TOK_STATEMENT_INNER:	return "STATEMENT_INNER"; 
		case TOK_FNCALL_ARGLIST:	return "FNCALL_ARGLIST"; 
		case TOK_FNDEF_ARGLIST:		return "FNDEF_ARGLIST";
		case TOK_DIM_VAR:			return "DIM_VAR";
		case TOK_TYPE_IDENTIFIER:	return "TYPENAME";
		case TOK_NUMERIC_LITERAL:	return "NUMERIC_LITERAL";
		case TOK_FNDEF:				return "FNDEF"; 
		case TOK_FNCALL:			return "FNCALL"; 
		case TOK_RVALUE:			return "RVALUE"; 
		case TOK_RVALUE_INNER:      return "RVALUE_INNER";
		case TOK_RVALUE_INNER2:		return "RVALUE_INNER2";
		case TOK_LVALUE:			return "LVALUE";
		case TOK_ASSIGNMENT:		return "ASSIGNMENT"; 
		case TOK_SLICE:				return "SLICE";
		case TOK_SLICE_INNER:		return "SLICE_INNER";
		case TOK_VECTOR_LITERAL:	return "VECTOR_LITERAL";
		case TOK_VECTOR_ELEMENT:	return "VECTOR_ELEMENT";
		case TOK_CBRACE_OPEN:		return "L{"; 
		case TOK_CBRACE_CLOSE:		return "L}"; 
		case TOK_SBRACE_OPEN:		return "L["; 
		case TOK_SBRACE_CLOSE:		return "L]"; 
		case TOK_PARENS_OPEN:		return "L("; 
		case TOK_PARENS_CLOSE:		return "L)"; 
		case TOK_OP_QUOTE:			return "L\""; 
		case TOK_OP_COMMA:			return "L,"; 
		case TOK_OP_COLON:			return "L:"; 
		case TOK_OP_SEMICOLON:		return "L;"; 
		case TOK_OP_EQUALS:			return "L="; 
		case TOK_OP_PLUS:			return "L+"; 
		case TOK_OP_MINUS:			return "L-"; 
		case TOK_OP_STAR:			return "L*"; 
		case TOK_OP_FORWARDSLASH:	return "L/"; 
		case TOK_OP_BACKSLASH:		return "L\\"; 
		case TOK_OP_PERCENT:		return "L%"; 
		case TOK_OP_LESSTHAN:       return "L<";
		case TOK_OP_GREATERTHAN:    return "L>";
		case TOK_IDENTIFIER:		return "IDENTIFIER"; 
		case TOK_QUOTED_STRING:		return "QUOTED_STRING"; 
		case TOK_BINARY_OPERATOR:	return "BINARY_OPERATOR";
		case TOK_RETURN_STATEMENT:  return "RETURN_STATEMENT";
		case TOK_IF_STATEMENT:      return "IF_STATEMENT";
		case TOK_RWORD_DO:			return "L-do";
		case TOK_RWORD_FOR:			return "L-for";
		case TOK_RWORD_IF:			return "L-if";
		case TOK_RWORD_ELSE:        return "L-else";
		case TOK_RWORD_RETURN:		return "L-return";
		case TOK_RWORD_WHILE:		return "L-while";
		case TOK_RWORD_INCLUDE:		return "L-include";
		case TOK_RWORD_BREAK:		return "L-break";
		case TOK_RWORD_CONTINUE:	return "L-continue";
		default: 					return "#unknown"; 
	}
}

void pexl_pprint_ast(token* t, int indent) {
	char* buff = NULL;
	if (indent) {
		buff = new char[indent + 1];
		memset(buff, ' ', indent);
		buff[indent] = '\0';
	} else {
		buff = ""; // const .rodata
	}

	fprintf(stderr, "%s%s", buff, pexl_pprint_typename(t->type));
	if (t->data.length()) {
		fprintf(stderr, " = \"%s\"", t->data.c_str());
	}
	if (t->vartype.is_valid()) {
		fprintf(stderr, " @type %s", t->vartype.serialize().c_str());
	}
	if (t->children.size()) {
		fprintf(stderr, " =>");
	}
	fprintf(stderr, "\n");

	if (t->children.size()) {
		for (int i = 0, e = t->children.size(); i < e; i++) {
			pexl_pprint_ast(t->children[i], indent + 2);
		}
		//printf("%s}", buff);
	}
	if (!t->data.length() && !t->children.size()) {
		fprintf(stderr, "\n");
	}

	if (indent) delete[] buff;
}

//
// AST FUNCTIONS
//

void IAstMapper::walk(token* t) {
	if (!this->action(t)) {
		return;
	}

	// Walk child nodes.
	// (these might be OP_ or other boring things).
	for (int i = 0, e = t->children.size(); i < e; i++) {
		this->walk(t->children[i]);
	}

	return;
}

//
// TYPE FUNCTIONS
//

bool PXType::is_inferable() {
	return is_scalar("var"); //(children.size() == 0 && type == "var");
}

bool PXType::is_valid() {
	return (this->type.length() > 0);
}

bool PXType::is_scalar() {
	return (children.size() == 0);
}

bool PXType::is_scalar(const char* type) {
	return (children.size() == 0 && ! this->type.compare(type));
}

bool PXType::equals(const PXType& other) {
	if ( type != other.type ||
		 children.size() != other.children.size()
	) return false;
	
	for (int i = 0, e = children.size(); i < e; i++) {
		if (! children[i].equals(other.children[i])) return false;
	}
	return true;
}

PXType PXType::make_scalar(const char* type) {
	PXType ret;
	ret.type.assign(type);
	ret.children.clear();
	return ret;
}

std::string PXType::serialize() {
	std::string ret = this->type;
	if (this->children.size()) {
		ret += "<" + this->children[0].serialize();
		for (int i = 1, e = this->children.size(); i < e; i++) {
			ret += "," + this->children[i].serialize();
		}
		ret += ">";
	}
	return ret;
}

bool PXType::unserialize(struct token* token, PXType& ret) {
	if (token->type != TOK_TYPE_IDENTIFIER) {
		fprintf(stderr, "Invalid token type passed to PXType::unserialize();\n");
		return false;
	}

	// type-identifier := literal_typename [ "<" type-identifier-list ">" ]
	// type-identifier-list := type-identifier [ "," type-identifier-list ]
	
	ret.type = token->children[0]->data;
	ret.children.clear();

	while (token->children.size() > 1) {
		token = token->children[2]; //->children[0];
		PXType child;
		if (unserialize(token->children[0], child)) {
			ret.children.push_back(child);
		} else {
			fprintf(stderr, "wat\n");
		}
	}

	return true;
}

bool PXType::unserialize(std::string& typestr, PXType& ret) {
	if (typestr.find('<') != typestr.npos) {
		fprintf(stderr, "PXType::unserialize doesn't support generic type '%s'!\n", typestr.c_str());
		return false;
	}

	ret = make_scalar(typestr.c_str());
	return true;
}

//
// STRING FUNCTIONS
//

std::string strf(const char* fmt, ...) {
	char buff[512] = {0};
	va_list va;
	va_start(va, fmt); {		
		vsprintf_s(buff, fmt, va);
	} va_end(va);
	return buff;
}

std::string escape_string(std::string& in) {

	// Produce output in the form `Hello\x2c\x20world\x11\x0a`
	// (nasm interpolates \x constants only with backquote-enclosed strings)
	std::string ret;
	ret.reserve(in.length() + 2); // at least

	ret += '`';
	for (size_t i = 0, e = in.length(); i != e; ++i) {
		if (isalnum(in[i]) || in[i] == ' ') {
			ret += in[i];
		} else {
			ret += strf("\\x%02X", in[i]);
		}
	}
	ret += '`';

	return ret;

}

std::string make_uniqid() {
	static unsigned int ctr = 0;
	return strf("%08u", ++ctr);
}
