#include "Core.h"

#define LTIMING(x)

static bool sOperatorTab[256];

INITBLOCK {
	for(const char *s = "!+-*^/%~&|=[]:?."; *s; s++)
		sOperatorTab[(int)*s] = true;
}

bool IsCppKeyword(const String& id)
{
	static Index<String> kw = {
        "__asm", "__cdecl", "__declspec", "__except", "__fastcall",
        "__finally", "__inline", "__int16", "__int32", "__int64",
        "__int8", "__leave", "__stdcall", "__try", "__uuidof",
        "alignas", "alignof", "and", "and_eq", "asm", "auto",
        "bitand", "bitor", "bool", "break", "case", "catch",
        "char", "char8_t", "char16_t", "char32_t", "class",
        "co_await", "co_return", "co_yield", "compl", "concept",
        "const", "const_cast", "consteval", "constexpr", "constinit",
        "continue", "decltype", "default", "delete", "dllexport",
        "dllimport", "do", "double", "dynamic_cast", "else", "enum",
        "explicit", "export", "extern", "false", "final", "float",
        "for", "force_inline", "friend", "goto", "if", "import",
        "inline", "int", "long", "module", "mutable", "namespace",
        "never_inline", "new", "noexcept", "not", "not_eq", "nullptr",
        "operator", "or", "or_eq", "override", "private", "protected",
        "public", "register", "reinterpret_cast", "requires", "return",
        "short", "signed", "sizeof", "static", "static_assert",
        "static_cast", "struct", "switch", "template", "this", "thread",
        "thread_local", "throw", "true", "try", "typedef", "typeid",
        "typename", "union", "unsigned", "using", "virtual",
        "void", "volatile", "wchar_t", "while", "xor", "xor_eq"
	};
	return kw.Find(id) >= 0;
}

bool IsBasicType(const String& id)
{
	static Index<String> kt = { "__int16", "__int32", "__int64", "__int8", "auto", "char",
	                            "char8_t", "char16_t", "char32_t", "double", "float", "int",
	                            "long", "short", "unsigned", "void", "wchar_t", "bool" };
	return kt.Find(id) >= 0;
}

bool IsIgnored(const String& id)
{
	static Index<String> kt = { "class", "struct", "union", "noexcept", "override", "final", "template", "enum", "__attribute__" };
	return kt.Find(id) >= 0;
}

String CleanupId(const char *s)
{ // removes unnecessary spaces, removes parameter names, makes clang ids consistent
	StringBuffer mm;
	bool was_param_type = false;
	bool was_id = false;
	bool was_space = false;
	bool was_name = false;
	bool function = false;
	bool destructor = false;
	bool inparams = false;
	int name_pos = 0; // to filter out eventual return value of function
	bool operator_def = false; // between operator and ( - suppress < > handling
	static String s_attribute = "__attribute__";
	auto SkipT = [&] {
		int lvl = 1;
		s++;
		while(*s && lvl) {
			if(*s == '>')
				lvl--;
			if(*s == '<')
				lvl++;
			s++;
		}
	};
	while(*s && *s != '{') {
		if(iscid(*s)) {
			auto IsOperator = [](const char *s) {
				return memcmp(s, "operator", 8) == 0;
			};

			String id;
			for(;;) {
				if(iscid(*s)) {
					id.Cat(*s++);
					if(*s == '<') {
						if(id.GetCount() == 8 && IsOperator(id))
							break;
						if(id.GetCount() > 8) {
							const char *s = ~id + id.GetCount() - 8;
							if(IsOperator(s) && !iscid(s[-1]))
								break;
						}
						SkipT(); // Skip template arguments like in Foo<Bar>::Method() -> Foo::Method
					}
				}
				else
				if(*s == ':' && s[1] == ':') {
					if(inparams)
						id.Clear(); // remove qualification from parameters (it is inconsistent with libclang)
					else {
						id.Cat(':');
						id.Cat(':');
					}
					s += 2;
				}
				else
					break;
			}
			if(id == s_attribute) {
				while(*s == ' ')
					s++;
				if(*s == '(') {
					s++;
					int lvl = 1;
					while(*s && lvl) {
						if(*s == '(')
							lvl++;
						if(*s == ')')
							lvl--;
						s++;
					}
					while(*s == ' ')
						s++;
				}
			}
			if((*s == ',' || *s == ')' || *s == '[') && was_param_type) {
				was_param_type = false;
				continue;
			}
			if(IsIgnored(id))
				continue;
			if(was_id)
				mm.Cat(' ');
			if(!operator_def) // because of conversion operators e.g. Foo::operator bool()
				name_pos = mm.GetCount();
			if(id.GetCount() == 8 && IsOperator(id))
				operator_def = true;
			if(id.GetCount() > 8) {
				const char *s = ~id + id.GetCount() - 8;
				operator_def = IsOperator(s) && !iscid(s[-1]);
			}
			if(function && (IsBasicType(id) || !IsCppKeyword(id)))
				was_param_type = true;
			mm.Cat(id);
			was_id = true;
			was_name = true;
		}
		else
		if(*s == ' ') { // filter out spaces that are not necessary
			was_space = true;
			s++;
		}
		else
		if(*s == '<' && !operator_def) { // remove template stuff e.g. Buffer<T>(Vector<T>) -> Buffer(Vector);
			SkipT();
		}
		else
		if(*s == '-' && s[1] == '>') // ignore return value in -> notation (auto Foo() -> double)
			return TrimRight(mm);
		else {
			was_id = was_space = false;
			if(*s == '~' && !operator_def) // prevent culling of 'return value' in destructor
				destructor = true;
			if(*s == '(') {
				if(!inparams) {
					if(name_pos && !destructor) {
						String h = String(mm).Mid(name_pos);
						mm = h;
					}
					function = true;
					was_param_type = false;
					operator_def = false;
				}
				if(s[1] == '*' && inparams) { // function pointer, e.g. int(*)(int)
					was_param_type = false;  // prevent skipping   ^^^
					mm.Cat('(');
					while(*s && *s != ')') {
						if(*s == '*') // exclude any names
							mm.Cat(*s);
						s++;
					}
					mm.Cat(')');
					if(*s) s++;
					continue;
				}
				inparams = true;
			}
			if(*s == ',')
				was_param_type = false;
			if((*s == '*' || *s == '&') && !was_name) // skip *& before parameter list
				s++;
			else
				mm.Cat(*s++);
		}
	}
	String m = mm;
	m.TrimEnd("=0");
	return m;
}

String CleanupPretty(const String& signature)
{
	LTIMING("CleanupPretty");
	StringBuffer result;
	const char *s = signature;
	int plvl = 0;
	int tlvl = 0;
	while(*s && *s != '{')
		if(s[0] == ' ' && s[1] == '=' && s[2] == ' ' && s[3] == '{')
			break;
		else
		if(s[0] == '(' && s[1] == 'u' && s[2] == 'n' && s[3] == 'n' && s[4] == 'a' &&
		   s[5] == 'm' && s[6] == 'e' && s[7] == 'd' && s[8] == ')') { // remove (unnamed)
			s += 9;
			while(*s == ' ')
				s++;
		}
		else
		if(iscib(*s)) {
			const char *b = s;
			while(iscid(*s))
				s++;
			int len = int(s - b);
			if(len == 13 && memcmp(b, "__attribute__", 13) == 0) {
				while(*s == ' ')
					s++;
				if(*s == '(') {
					s++;
					int lvl = 1;
					while(*s && lvl) {
						if(*s == '(')
							lvl++;
						if(*s == ')')
							lvl--;
						s++;
					}
					while(*s == ' ')
						s++;
				}
				continue;
			}
			if(len == 5 && (memcmp(b, "class", 5) == 0 && tlvl == 0 || memcmp(b, "union", 5) == 0) ||
			   len == 6 && (memcmp(b, "struct", 6) == 0 && tlvl == 0 || memcmp(b, "extern", 6) == 0 || memcmp(b, "inline", 6) == 0) ||
			   len == 7 && (memcmp(b, "virtual", 7) == 0) ||
			   len == 8 && (memcmp(b, "override", 8) == 0 || memcmp(b, "noexcept", 8) == 0)) {
			   while(*s == ' ')
			       s++;
			   continue;
			}
			result.Cat(b, s);
		}
		else
		if(s[0] == ' ' && s[1] == '*' && s[2] == ' ') {
			result.Cat(' ');
			result.Cat('*');
			s += 3;
		}
		else
		if(s[0] == ' ' && s[1] == '&' && s[2] == ' ') {
			result.Cat('&');
			result.Cat(' ');
			s += 3;
		}
		else
		if(s[0] == ' ' && s[1] == '&' && s[2] == '&' && s[3] == ' ') {
			result.Cat('&');
			result.Cat('&');
			result.Cat(' ');
			s += 4;
		}
		else
		if(s[0] == ' ' && s[1] == '&' && s[2] == '&') {
			result.Cat('&');
			result.Cat('&');
			if(iscib(s[3]))
				result.Cat(' ');
			s += 3;
		}
		else
		if(s[0] == ' ' && s[1] == '&') {
			result.Cat('&');
			if(iscib(s[2]))
				result.Cat(' ');
			s += 2;
		}
		else
		if(*s == ' ') {
			while(*s == ' ')
				s++;
			result.Cat(' ');
		}
		else
		if(s[0] == '-' && s[1] == '>')
			break;
		else
		if(*s == '=' && plvl == 0)
			break;
		else {
			if(*s == '(') plvl++;
			if(*s == ')') plvl--;
			if(*s == '<') tlvl++;
			if(*s == '>') tlvl--;
			result.Cat(*s++);
		}
	String r = result;
	while(*r.Last() == ' ')
		r.TrimLast();
	return r;
}

Vector<ItemTextPart> ParsePretty(const String& name, const String& signature, int *fn_info)
{
	bool op = strncmp(name, "operator", 8) == 0 && !iscid(name[8]);
	Vector<ItemTextPart> part;
	int name_len = name.GetLength();
	if(name_len == 0) {
		ItemTextPart& p = part.Add();
		p.pos = 0;
		p.len = signature.GetLength();
		p.type = ITEM_TEXT;
		p.pari = -1;
		return part;
	}
	bool param = false;
	bool was_type = false;
	bool was_colon = false;
	int pari = -1;
	int par = 0;
	int lastidi = -1;
	const char *s = signature;
	if(fn_info)
		*fn_info = -1;
	while(*s) {
		ItemTextPart& p = part.Add();
		p.pos = (int)(s - ~signature);
		p.type = ITEM_TEXT;
		p.pari = pari;

		auto Pname = [&] {
			if(lastidi >= 0) {
				if(was_type && !was_colon)
					part[lastidi].type = ITEM_PNAME;
				lastidi = -1;
			}
			was_type = false;
		};
		
		if(*s == ':')
			was_colon = true;

		int n = 1;
		if(*s >= '0' && *s <= '9') {
			while(s[n] >= '0' && s[n] <= '9' || (s[n] == 'x' || s[n] == 'X'))
				n++;
			p.type = ITEM_NUMBER;
		}
		else
		if(iscid(*s) || (*s == '~' && *name == '~')) {
			if(strncmp(s, name, name_len) == 0 && !iscid(s[name_len]) && !op) {
				p.type = ITEM_NAME;
				n = name_len;
				const char *q = s + name_len;
				while(*q == ' ')
					q++;
				if(*q == '(') {
					param = true;
					if(fn_info)
						*fn_info = 0;
				}
			}
			else
			if(*s != '~') {
				String id;
				n = 0;
				while(iscid(s[n]))
					id.Cat(s[n++]);
				if(IsBasicType(id)) {
					if(param && par == 0)
						was_type = true;
					p.type = ITEM_CPP_TYPE;
				}
				else
				if(id == "operator")
					p.type = ITEM_OPERATOR;
				else
				if(IsCppKeyword(id)) {
					if(id == "virtual")
						part.Drop();
					else
						p.type = ITEM_CPP;
				}
				else
				if(param) {
					if(lastidi >= 0 && par == 0 && was_colon)
						was_colon = false;
					else
					if(lastidi >= 0 && par == 0)
						was_type = true;
					lastidi = part.GetCount() - 1; // can be a parameter name
				}
				if(param && fn_info)
					*fn_info = 1;
			}
			else // should not happen, but be safe
				s++;
		}
		else
		if(sOperatorTab[(byte)*s]) {
			while(sOperatorTab[(byte)s[n]])
				n++;
			if(*s == '=' && par == 0) {
				Pname();
				p.pari = -1;
				pari++;
			}
			p.type = ITEM_CPP;
		}
		else {
			p.type = ITEM_SIGN;
			if(pari >= 0) {
				if(*s == '(' || *s == '<')
					par++;
				if(*s == ')') {
					par--;
					if(par < 0) {
						p.pari = -1;
						pari = -1;
						param = false;
						Pname();
					}
				}
				if(*s == '>')
					par--;
				if(*s == ',' && par == 0) {
					Pname();
					p.pari = -1;
					pari++;
				}
			}
			else
			if(*s == '(' && param) {
				pari = 0;
				par = 0;
			}
		}
		p.len = n;
		s += n;
	}
	return part;
}

Image CxxIcon(int kind);

String SignatureQtf(const String& name, const String& pretty, const String& nest, int pari)
{
	String qtf = "[%00-00K ";
	Vector<ItemTextPart> n = ParsePretty(name, pretty);
	String ns = nest;
	for(int i = 0; i < n.GetCount(); i++) {
		ItemTextPart& p = n[i];
		qtf << "[";
		if(p.pari == pari)
			qtf << "$y";
		switch(p.type) {
		case ITEM_PNAME:
			qtf << "*";
		case ITEM_NUMBER:
			qtf << "@r";
			break;
		case ITEM_TNAME:
			qtf << "*@g";
			break;
		case ITEM_NAME:
			qtf << "*";
			break;
		case ITEM_UPP:
			qtf << "@c";
			break;
		case ITEM_CPP_TYPE:
		case ITEM_CPP:
			qtf << "@B";
			break;
		}
		qtf << ' ';
		if(ns.GetCount() && p.type == ITEM_NAME) {
			qtf << "[@b \1" << ns << "::\1]";
			ns.Clear();
		}
		qtf << '\1' << pretty.Mid(p.pos, p.len) << '\1';
		qtf << ']';
	}
	return qtf + "]";
}
