#pragma once
#include "token.h"
#include "../output_helpers.h"

SCENE_TOKEN_TYPE::SCENE_TOKEN_TYPE(int a, int b, int c, int d) : _a(a), _b(b), _c(c), _d(d) {}

SCENE_TOKEN_TYPE SCENE_TOKEN_TYPE::operator|(const SCENE_TOKEN_TYPE& o) const {
	return SCENE_TOKEN_TYPE(_a|o._a, _b|o._b, _c|o._c, _d|o._d);
}
bool SCENE_TOKEN_TYPE::operator&(const SCENE_TOKEN_TYPE& o) const {
	return (_a&o._a | _b&o._b | _c&o._c | _d&o._d)!=0;
}
bool SCENE_TOKEN_TYPE::operator==(const SCENE_TOKEN_TYPE& o) const {
	return (_a==o._a)&&(_b==o._b)&&(_c==o._c)&&(_d==o._d);
}
bool SCENE_TOKEN_TYPE::operator<(const SCENE_TOKEN_TYPE& o) const {
	if (_a==o._a) {
		if (_b==o._b) {
			if (_c==o._c) {
				return _d<o._d;
			}
			return _c<o._c;
		}
		return _b<o._b;
	}
	return _a<o._a;
}
void SCENE_TOKEN_TYPE::increment(void) {
	if (_d==0x80000000) {
		_d = 0;
		if (_c==0x80000000) {
			_c = 0;
			if (_b==0x80000000) {
				_b = 0;
				_a *= 2;
			}
			else _b *= 2;
		}
		else _c *= 2;
	}
	else if (_d==0x00000000) _d = 1;
	else _d *= 2;
}

#ifndef DEBUG_TOKENS
void print_hex(int digit) {
	printf("0x%08x",digit);
}
void SCENE_TOKEN_TYPE::print(void) {
	printf("(");
	print_hex(_a);printf(",");
	print_hex(_b);printf(",");
	print_hex(_c);printf(",");
	print_hex(_d);
	printf(")");
}
#else
void SCENE_TOKEN_TYPE::print(void) {
	printf("(\"%s\")",Token::get_name(this)+4);
}
#endif
string SCENE_TOKEN_TYPE::get_as_string(void) {
	return string("(\"") + (Token::get_name(this)+4) + "\")";
}

void Token::init(void) {
	regexes = new type_token_regexes();
	#ifdef DEBUG_TOKENS
	names = new type_token_names();
	#endif

	(*regexes)[STT_BLOCK_START] = new Regex("\\{");
	(*regexes)[  STT_BLOCK_END] = new Regex("\\}");
	#ifdef DEBUG_TOKENS
	(*names)[STT_BLOCK_START] = new string("STT_BLOCK_START");
	(*names)[  STT_BLOCK_END] = new string(  "STT_BLOCK_END");
	#endif

	(*regexes)[STT_WHITESPACE] = new Regex("[ \\t]+");
	(*regexes)[   STT_NEWLINE] = new Regex("\\\n");
	(*regexes)[       STT_EOF] = new Regex("$");
	#ifdef DEBUG_TOKENS
	(*names)[STT_WHITESPACE] = new string("STT_WHITESPACE");
	(*names)[   STT_NEWLINE] = new string(   "STT_NEWLINE");
	(*names)[       STT_EOF] = new string(       "STT_EOF");
	#endif

	(*regexes)[STT_INHERITANCE] = new Regex(":");
	#ifdef DEBUG_TOKENS
	(*names)[STT_INHERITANCE] = new string("STT_INHERITANCE");
	#endif

	(*regexes)[STT_INTEGER] = new Regex("\\d+");
	(*regexes)[STT_DECIMAL] = new Regex("(\\d+\\.\\d+|\\d+\\.|\\.\\d+)");
	#ifdef DEBUG_TOKENS
	(*names)[STT_INTEGER] = new string("STT_INTEGER");
	(*names)[STT_DECIMAL] = new string("STT_DECIMAL");
	#endif

	(*regexes)[STT_STRING] = new Regex("\\\".+\\\"");
	#ifdef DEBUG_TOKENS
	(*names)[STT_STRING] = new string("STT_STRING");
	#endif

	(*regexes)[       STT_COMMENT_LINE] = new Regex("//[^\\n]*");
	(*regexes)[STT_COMMENT_BLOCK_START] = new Regex("/\\*");
	(*regexes)[  STT_COMMENT_BLOCK_END] = new Regex("(.|\\s)+\\*/");
	#ifdef DEBUG_TOKENS
	(*names)[       STT_COMMENT_LINE] = new string(       "STT_COMMENT_LINE");
	(*names)[STT_COMMENT_BLOCK_START] = new string("STT_COMMENT_BLOCK_START");
	(*names)[  STT_COMMENT_BLOCK_END] = new string(  "STT_COMMENT_BLOCK_END");
	#endif

	(*regexes)[  STT_CONTEXT_CAMERA] = new Regex("camera");
	(*regexes)[   STT_CONTEXT_IMAGE] = new Regex("image");
	(*regexes)[   STT_CONTEXT_LIGHT] = new Regex("light");
	(*regexes)[STT_CONTEXT_MATERIAL] = new Regex("material");
	(*regexes)[  STT_CONTEXT_OBJECT] = new Regex("object");
	(*regexes)[   STT_CONTEXT_SCENE] = new Regex("scene");
	#ifdef DEBUG_TOKENS
	(*names)[  STT_CONTEXT_CAMERA] = new string(  "STT_CONTEXT_CAMERA");
	(*names)[   STT_CONTEXT_IMAGE] = new string(   "STT_CONTEXT_IMAGE");
	(*names)[   STT_CONTEXT_LIGHT] = new string(   "STT_CONTEXT_LIGHT");
	(*names)[STT_CONTEXT_MATERIAL] = new string("STT_CONTEXT_MATERIAL");
	(*names)[  STT_CONTEXT_OBJECT] = new string(  "STT_CONTEXT_OBJECT");
	(*names)[   STT_CONTEXT_SCENE] = new string(   "STT_CONTEXT_SCENE");
	#endif

	(*regexes)[  STT_ATTRIB_IMAGE_WIDTH] = new Regex("width");
	(*regexes)[ STT_ATTRIB_IMAGE_HEIGHT] = new Regex("height");
	(*regexes)[STT_ATTRIB_IMAGE_SAMPLES] = new Regex("samples");
	#ifdef DEBUG_TOKENS
	(*names)[  STT_ATTRIB_IMAGE_WIDTH] = new string(  "STT_ATTRIB_IMAGE_WIDTH");
	(*names)[ STT_ATTRIB_IMAGE_HEIGHT] = new string( "STT_ATTRIB_IMAGE_HEIGHT");
	(*names)[STT_ATTRIB_IMAGE_SAMPLES] = new string("STT_ATTRIB_IMAGE_SAMPLES");
	#endif

	(*regexes)[STT_ATTRIB_CAMERA_POSITION] = new Regex("position");
	(*regexes)[  STT_ATTRIB_CAMERA_CENTER] = new Regex("center");
	(*regexes)[      STT_ATTRIB_CAMERA_UP] = new Regex("up");
	#ifdef DEBUG_TOKENS
	(*names)[STT_ATTRIB_CAMERA_POSITION] = new string("STT_ATTRIB_CAMERA_POSITION");
	(*names)[  STT_ATTRIB_CAMERA_CENTER] = new string(  "STT_ATTRIB_CAMERA_CENTER");
	(*names)[      STT_ATTRIB_CAMERA_UP] = new string(      "STT_ATTRIB_CAMERA_UP");
	#endif

	(*regexes)[    STT_ATTRIB_CAMERA_APERTURE] = new Regex("aperture");
	(*regexes)[ STT_ATTRIB_CAMERA_IMAGE_PLANE] = new Regex("image_plane");
	(*regexes)[STT_ATTRIB_CAMERA_FOCAL_LENGTH] = new Regex("focal");
	#ifdef DEBUG_TOKENS
	(*names)[    STT_ATTRIB_CAMERA_APERTURE] = new string(    "STT_ATTRIB_CAMERA_APERTURE");
	(*names)[ STT_ATTRIB_CAMERA_IMAGE_PLANE] = new string( "STT_ATTRIB_CAMERA_IMAGE_PLANE");
	(*names)[STT_ATTRIB_CAMERA_FOCAL_LENGTH] = new string("STT_ATTRIB_CAMERA_FOCAL_LENGTH");
	#endif

	(*regexes)[STT_ATTRIB_COLOR] = new Regex("color");
	#ifdef DEBUG_TOKENS
	(*names)[STT_ATTRIB_COLOR] = new string("STT_ATTRIB_COLOR");
	#endif
}
void Token::deinit(void) {
	for (type_token_regexes::iterator iter=regexes->begin(); iter!=regexes->end(); ++iter) {
		delete iter->second;
	}
	delete regexes;

	#ifdef DEBUG_TOKENS
	for (type_token_names::iterator iter=names->begin(); iter!=names->end(); ++iter) {
		delete iter->second;
	}
	delete names;
	#endif
}

string get_pointer_as_string(void* pointer) {
	char output[100]; for (int i=0;i<100;++i) output[i] = '\0'; sprintf(output,"%p",pointer);
	return string("0x")+output;
}
Token::Token(string new_content, SCENE_TOKEN_TYPE new_type) {
	content = new string(new_content);
	type = new SCENE_TOKEN_TYPE(new_type);

	/*output_debug(
		"New: ("+
		get_pointer_as_string(content)+") ("+
		get_pointer_as_string(type)+") -> ("+
		get_pointer_as_string(this)+")",0
	);*/
	//output_debug("ALLOCATING ONE TOKEN AT ("+get_pointer_as_string(this)+")",0);
}
Token::Token(Token& t) {
	content = new string(*t.content);
	type = new SCENE_TOKEN_TYPE(*t.type);

	/*output_debug(
		"Copy: ("+
		get_pointer_as_string(this)+") contains: ("+
		get_pointer_as_string(content)+") ("+
		get_pointer_as_string(type)+")",0
	);*/
	//output_debug("COPYING ONE TOKEN FROM ("+get_pointer_as_string(&t)+") TO ("+get_pointer_as_string(this)+")",0);
}
Token::~Token() {
	/*output_debug(
		"Delete: ("+
		get_pointer_as_string(content)+") ("+
		get_pointer_as_string(type)+") -> ("+
		get_pointer_as_string(this)+")",0
	);*/
	//output_debug("DELETING ONE TOKEN ("+get_pointer_as_string(this)+")",0);

	delete content;
	delete type;
}

string Token::get_content_printable(void) {
	string result;
	for (string::const_iterator iter=content->begin(); iter!=content->end(); ++iter) {
		char c = *iter;
		switch (c) {
			case '\0': result += "\\0"; break;
			case '\n': result += "\\n"; break;
			case '\t': result += "\\t"; break;
			default: result += c; break;
		}
	}
	return result;
}

const char* Token::get_name(SCENE_TOKEN_TYPE* t) {
	return (*Token::names)[*t]->c_str();
}

type_token_regexes* Token::regexes;
type_token_names* Token::names;