import "std_String.jnc"

// port of axl_enc_EscapeEncoding.cpp

//! \addtogroup common-enc
//! @{

//..............................................................................

char findEscapeReplaceChar(char x) {
	switch (x) {
	case '0':
		return '\0';

	case 'a':
		return '\a';

	case 'b':
		return '\b';

	case 't':
		return '\t';

	case 'n':
		return '\n';

	case 'f':
		return '\f';

	case 'v':
		return '\v';

	case 'r':
		return '\r';

	case 'e':
		return '\x1b';

	default:
		return x;
	};
}

size_t decodeEscapeString(
	std.StringBuilder* string,
	string_t source
) {
	enum State {
		Normal = 0,
		Escape,
		Hex,
	};

	State state = State.Normal;

	string.clear();
	string.reserve(source.m_length / 2);

	char hexCodeString[16];
	size_t hexCodeLen;
	size_t hexCodeMaxLen;
	uint32_t hexCode;

	char replace;

	char const* p = source.m_p;
	char const* end = p + source.m_length;
	char const* base = p;

	for (; p < end; p++) {
		switch (state) {
		case State.Normal:
			if (*p == '\\') {
				string.append(base, p - base);
				state = State.Escape;
			}

			break;

		case State.Escape:
			switch (*p) {
			case 'x':
				state = State.Hex;
				hexCodeLen = 0;
				hexCodeMaxLen = 2;
				break;

			case 'u':
				state = State.Hex;
				hexCodeLen = 0;
				hexCodeMaxLen = 4;
				break;

			case 'U':
				state = State.Hex;
				hexCodeLen = 0;
				hexCodeMaxLen = 8;
				break;

			default:
				replace = findEscapeReplaceChar(*p);
				if (replace != *p) {
					string.append(replace);
					base = p + 1;
				} else {
					base = p;
				}

				state = State.Normal;
			}

			break;

		case State.Hex:
			char c = *p;
			if (c >= '0' && c <= '9' ||
				c >= 'a' && c <= 'f' ||
				c >= 'A' && c <= 'F') {
				hexCodeString[hexCodeLen++] = c;
				if (hexCodeLen < hexCodeMaxLen)
					break;

				base = p + 1;
			} else {
				base = p;
			}

			if (!hexCodeLen) {
				string.append('?');
			} else {
				hexCodeString[hexCodeLen] = 0;
				hexCode = strtol(hexCodeString, null, 16);

				if (hexCodeMaxLen == 2) // \x
					string.append((char const*)&hexCode, 1);
				else // \u or \U
					string.append(hexCode);
			}

			state = State.Normal;
			break;
		}
	}

	string.append(base, p - base);
	return string.m_length;
}

//..............................................................................

//! @}
