#include "GPArchive.h"
#include "ResTypeID.h"

#include <string.h>

static const char *gs_forbiddenNames[] =
{
	"COM1",
	"COM2",
	"COM3",
	"COM4",
	"COM5",
	"COM6",
	"COM7",
	"COM8",
	"COM9",
	"LPT1",
	"LPT2",
	"LPT3",
	"LPT4",
	"LPT5",
	"LPT6",
	"LPT7",
	"LPT8",
	"LPT9",
};

static bool IsCharForbidden(char c)
{
	if ((c & 0x80) != 0)
		return true;

	// <= '$' includes space, ! " # $
	return (c <= '$' || c == '<' || c == '>' || c == ':' || c == '\"' || c == '/' || c == '\\' || c == '|' || c == '?' || c == '*' || c > '~');
}

namespace PortabilityLayer
{
	GpArcResourceTypeTag GpArcResourceTypeTag::Encode(const ResTypeID &tag)
	{
		static const char *nibbles = "0123456789abcdef";

		char chars[4];
		tag.ExportAsChars(chars);

		GpArcResourceTypeTag output;
		memset(&output, 0, sizeof(output));

		char *outChar = output.m_id;
		for (int i = 0; i < 4; i++)
		{
			char c = chars[i];

			bool isForbidden = IsCharForbidden(c);

			if (i == 3)
			{
				if (c == ' ' || c == '.')
					isForbidden = true;
				else
				{
					for (int fi = 0; fi < sizeof(gs_forbiddenNames) / sizeof(gs_forbiddenNames[0]); fi++)
					{
						if (!memcmp(chars, gs_forbiddenNames[fi], 4))
						{
							isForbidden = true;
							break;
						}
					}
				}
			}

			if (isForbidden)
			{
				uint8_t byteValue;
				memcpy(&byteValue, &c, 1);

				*outChar++ = '$';
				int highNibble = (byteValue >> 4) & 0xf;
				int lowNibble = (byteValue & 0xf);

				*outChar++ = nibbles[highNibble];
				*outChar++ = nibbles[lowNibble];
			}
			else
			{
				*outChar++ = c;
			}
		}

		return output;
	}

	bool GpArcResourceTypeTag::Load(const char *str, size_t l)
	{
		if (l < sizeof(m_id))
		{
			memcpy(m_id, str, l);
			m_id[l] = '\0';
			return true;
		}

		return false;
	}

	bool GpArcResourceTypeTag::Decode(ResTypeID &outTag)
	{
		char decodedChars[4];
		size_t parseOffset = 0;
		for (int i = 0; i < 4; i++)
		{
			char parseChar = m_id[parseOffset];
			if (parseChar == '\0')
				return false;

			if (parseChar == '$')
			{
				char nibbles[2] = { m_id[parseOffset + 1], m_id[parseOffset + 2] };
				parseOffset += 2;

				int decodedNibbles[2];

				for (int n = 0; n < 2; n++)
				{
					char nibble = nibbles[n];
					if (nibble >= '0' && nibble <= '9')
						decodedNibbles[n] = nibble - '0';
					else if (nibble >= 'a' && nibble <= 'f')
						decodedNibbles[n] = nibble - 'a' + 0xa;
					else
						return false;
				}

				decodedChars[i] = (decodedNibbles[0] << 4) | (decodedNibbles[1]);
			}
			else
				decodedChars[i] = parseChar;

			parseOffset++;
		}

		outTag = ResTypeID(decodedChars);
		return true;
	}
}
