#include "algo.h"

namespace algo{
	/* VARS */
	inline std::stack<struct frame> callstack;
	inline std::unordered_map<std::string, std::string> vars;
	/**/

	bool init(){
		const char *const *c = standard_vars-1;
		while((c++, *c != NULL)){
			vars[*c] = EMPTY_VAL;
		}
		callstack.emplace();

		return true;
	}

	void assign(const std::string &name, const std::string& val){
		vars[name] = val;
		//unsigned index = findf_relationship(NAME, v[ALGO::DESTINATION]);
		//if(index == JOKERTAO::INVALID){
		//	jt::table.emplace_back(JOKERTAO::NAME, v[ALGO::DESTINATION]);
		//	jt::table.emplace_back(JOKERTAO::VALUE, v[ALGO::VALUE], 0);
		//}else{
		//	jt[index].find_attribute()
		//}
	}

	void pop(unsigned &head){
		if(callstack.size() == 1){
			#ifdef DEBUG
				printf(YELLOW "Return on empty callstack: exiting.\n" NORMAL);
			#endif
			raise(SIGINT);
		}
		callstack.pop();
		head = callstack.top().bp + 1;
	}
	void push(const unsigned &head){
		callstack.top().bp = head;
		callstack.emplace();
	}

	std::string arref(std::string s){
		for(int i = s.size()-1, last = i; i >= 0; i--){
			if(s[i] == '-'){
				s.replace(i+1, last-i, deref(s.substr(i+1, last-i)));
			}
		}

		return s;
	}

	std::string deref(std::string s){
		s = arref(s);
		return (vars.find(s) != vars.end()) ? deref(vars[s]) : s;
	}

	bool jump(unsigned &head, const jmp &do_jump, const std::string &dest, const char &push){
		bool ret = false;
		switch(do_jump){
			case algo::jmp::nop:
				break;
			case algo::jmp::jmp:
				algo::call(head, dest, (push != algo::EMPTY_VAL));
				ret = true;
				break;
			case algo::jmp::je:
				if(algo::vars["eval"] == algo::true_){
					algo::call(head, dest, (push != algo::EMPTY_VAL));
					ret = true;
				}
				break;
			case algo::jmp::jne:
				if(algo::vars["eval"] == algo::false_){
					algo::call(head, dest, (push != algo::EMPTY_VAL));
					ret = true;
				}
				break;
		}
		#ifdef DEBUG
			if(ret){
				printf(MAGENTA "Jumped to '%s' (%s). " NORMAL, dest.c_str(), algo::deref(dest).c_str());
			}
			printf(MAGENTA "Head at %d. " NORMAL, head);
		#endif
		return ret;
	}

	void call(unsigned &head, const std::string &dest, const bool &is_push){
		#ifdef DEBUG
			long debug_stacklen = algo::callstack.size();
		#endif
		if(dest == "return"){
			pop(head);
			goto RETURN;
		}

		if(is_push){ push(head); }

		try{
			head = std::stoi(deref(dest));
		}catch(...){
			#ifdef DEBUG
				printf("Invalid jump destination '%s'", dest.c_str());
			#endif
			goto RETURN;
		}

		RETURN:
			#ifdef DEBUG
				if(algo::callstack.size() != debug_stacklen){
					printf(YELLOW "[Stack: %ld->%ld] " NORMAL, debug_stacklen, algo::callstack.size());
				}
			#endif
	}

	int aint(std::string s){
		static const char c[] = "0123456789";
		int i = 0;
		bool sign = true;
		while(true){
			switch(s[i]){
				case '-':
					sign = !(sign xor false);
					break;
				case '+':
					sign = !(sign xor true);
					break;
				default:
					goto BR;
			}
			++i;
		}
		BR:
		s.erase(0, i);
		
		for(i = 0; i < s.size(); i++){
			bool is_any = false;
			for(int h = 0, end2 = sizeof(c)-1; h < end2; h++){
				if(s[i] == c[h]){
					is_any = true;
					break;
				}
			}
			if(not is_any){
				s.erase(i, 1);
				--i;
			}
		}
		if(s == ""){ s = '0'; }

		return std::stoi(s) * (sign ? 1 : -1);
	}


	void do_eval(const std::string &opr1, char oper, const std::string &opr2){	// convert to use assign()
		#define CASECHAR(c){ \
			case (char)(*#c):\
				vars["eval"] = std::to_string(aint(deref(opr1)) c aint(deref(opr2))); \
		}
		#define CASELCHAR(c){ \
			case (char)(*#c):\
				aint(deref(opr1)) c aint(deref(opr2)) ? (vars["eval"] = true_) : (vars["eval"] = false_); \
		}
		//is_cmp = (oper == EMPTY_VAL);
		vars["eval"] = "";
		switch(oper){
			case EMPTY_VAL:
				vars["eval"] = deref(opr1);
				return;
			case '.':
				vars["eval"] = deref(opr1) + deref(opr2);
				return;
			case '\'':
				vars["eval"] = deref(opr1).substr(0, opr1.size()-aint(deref(opr2)));
				return;
			CASECHAR(-)
				return;
			CASECHAR(+)
				return;
			CASECHAR(/)
				return;
			CASECHAR(*)
				return;
			CASECHAR(%)
				return;
			CASELCHAR(<)
				return;
			CASELCHAR(>)
				return;
			case '=':
				aint(deref(opr1)) == aint(deref(opr2)) ? (vars["eval"] = true_) : (vars["eval"] = false_); \
				return;
			CASELCHAR(|)
				return;
			CASELCHAR(&)
				return;
			CASELCHAR(^)
				return;
		}
	}
}


	//int aint(std::string s){
	//	static const char c[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // ?!
	//	bool sign = true;
	//	int roof = 1;
	//	
	//	for(int i = 0, int end = s.size(); i < end; i++){
	//		int h = 0;
	//		for(int e = sizeof(c)-1; h < e; h++){
	//			if(c[h] == s[i]){
	//				while(pow(2, roof) < h){
	//					++roof;
	//				}
	//				goto CONT;
	//			}
	//		}
	//		switch(s[i]){
	//			case '-':
	//				sign = !(sign xor false);
	//				s.rease(i, 1);
	//				--i;
	//				continue;
	//			case '+':
	//				sign = !(sign xor true);
	//				s.rease(i, 1);
	//				--i;
	//				continue;
	//			default:
	//				s[i] = '0';
	//				continue;
	//		}
	//		CONT:
	//	}
	//}
