
#include <winpr/crt.h>
#include <winpr/winpr.h>
#include <winpr/collections.h>

/**
 * Ternary Raster Operations:
 * See "Windows Graphics Programming: Win32 GDI and DirectDraw", chapter 11. Advanced Bitmap
 * Graphics
 *
 * Operators:
 *
 * 	AND		&		a
 * 	OR		|		o
 * 	NOT		~		n
 * 	XOR		^		x
 *
 * Operands:
 *
 * 	Pen/Brush			P
 * 	Destination			D
 * 	Source				S
 *
 * Example:
 *
 * Raster operation which returns P if S is 1 or D otherwise:
 * (rop_S & rop_P) | (~rop_S & rop_D); -> 0xE2 (0x00E20746)
 *
 * Postfix notation: DSPDxax
 * Infix notation: D^(S&(P^D))), (S&P)|(~S&D)
 *
 * DSPDxax using D^(S&(P^D)):
 *
 * 	mov eax, P	// P
 * 	xor eax, D	// P^D
 * 	and eax, S	// S&(P^D)
 * 	xor eax, D	// D^(S&(P^D))
 * 	mov D, eax	// write result
 *
 * DSPDxax using (S&P)|(~S&D):
 *
 * 	mov eax, S	// S
 * 	and eax, P	// S&P
 * 	mov ebx, S	// S
 * 	not ebx		// ~S
 * 	and ebx, D	// ~D&D
 * 	or eax, ebx	// (S&P)|(~S&D)
 * 	mov D, eax	// write result
 *
 * Raster operation lower word encoding:
 *
 *  _______________________________________________________________________________
 * |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |
 * |   Op5   |   Op4   |   Op3   |   Op2   |   Op1   | Not| Parse String |  Offset |
 * |____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|____|
 *   15   14   13   12   11   10    9    8    7    6    5    4    3   2     1    0
 *
 * Operator:
 * 	0: NOT
 * 	1: XOR
 * 	2: OR
 * 	3: AND
 *
 * Parse String:
 * 	0: SPDDDDDD
 * 	1: SPDSPDSP
 * 	2: SDPSDPSD
 * 	3: DDDDDDDD
 * 	4: DDDDDDDD
 * 	5: S+SP-DSS
 * 	6: S+SP-PDS
 * 	7: S+SD-PDS
 *
 * The lower word for 0x00E20746 is 0x0746 (00000111 01000110)
 *
 * 00		Op5 (NOT, n)
 * 00		Op4 (NOT, n)
 * 01		Op3 (XOR, x)
 * 11		Op2 (AND, a)
 * 01		Op1 (XOR, x)
 * 0		Not (unused)
 * 001		String (SPDSPDSP)
 * 10		Offset (2)
 *
 * We shift SPDSPDSP to the left by 2: DSPDSPSP
 *
 * We have 5 operators: 3 binary operators and the last two are unary operators,
 * so only four operands are needed. The parse string is truncated to reflect
 * the number of operands we need: DSPD
 *
 * The operator string (from Op1 to Op5) is xaxnn, which can be simplified to xax
 *
 * The complete string representing the operation is DSPDxax
 *
 */

static char* gdi_convert_postfix_to_infix(const char* postfix)
{
	BOOL unary = 0;
	size_t al = 0;
	size_t bl = 0;
	wStack* stack = Stack_New(FALSE);
	size_t length = strlen(postfix);

	for (size_t i = 0; i < length; i++)
	{
		BOOL success = FALSE;
		if ((postfix[i] == 'P') || (postfix[i] == 'D') || (postfix[i] == 'S'))
		{
			/* token is an operand, push on the stack */
			char* a = malloc(2);
			if (!a)
				goto end;
			a[0] = postfix[i];
			a[1] = '\0';
			// printf("Operand: %s\n", a);
			Stack_Push(stack, a);
		}
		else
		{
			char* a = nullptr;
			char* b = nullptr;

			/* token is an operator */
			unary = FALSE;
			char* c = malloc(2);
			if (!c)
				goto fail;
			c[0] = postfix[i];
			c[1] = '\0';

			if (c[0] == 'a')
			{
				c[0] = '&';
			}
			else if (c[0] == 'o')
			{
				c[0] = '|';
			}
			else if (c[0] == 'n')
			{
				c[0] = '~';
				unary = TRUE;
			}
			else if (c[0] == 'x')
			{
				c[0] = '^';
			}
			else
			{
				printf("invalid operator: %c\n", c[0]);
			}

			// printf("Operator: %s\n", c);
			a = (char*)Stack_Pop(stack);
			if (!a)
				goto fail;

			if (!unary)
				b = (char*)Stack_Pop(stack);

			al = strlen(a);

			if (b)
				bl = strlen(b);

			size_t cl = 1;
			size_t dl = al + bl + cl + 3;
			char* d = malloc(dl + 1);
			if (!d)
				goto fail;

			(void)sprintf_s(d, dl, "(%s%s%s)", b ? b : "", c, a);
			Stack_Push(stack, d);

			success = TRUE;
		fail:
			free(a);
			free(b);
			free(c);
			if (!success)
				goto end;
		}
	}

	char* d = (char*)Stack_Pop(stack);
	Stack_Free(stack);
	return d;

end:
	Stack_Free(stack);
	return nullptr;
}

static const char* test_ROP3[] = { "DSPDxax",  "PSDPxax", "SPna",   "DSna",   "DPa",
	                               "PDxn",     "DSxn",    "PSDnox", "PDSona", "DSPDxox",
	                               "DPSDonox", "SPDSxax", "DPon",   "DPna",   "Pn",
	                               "PDna",     "DPan",    "DSan",   "DSxn",   "DPa",
	                               "D",        "DPno",    "SDno",   "PDno",   "DPo" };

int TestGdiRop3(int argc, char* argv[])
{
	WINPR_UNUSED(argc);
	WINPR_UNUSED(argv);

	for (size_t index = 0; index < sizeof(test_ROP3) / sizeof(test_ROP3[0]); index++)
	{
		const char* postfix = test_ROP3[index];
		char* infix = gdi_convert_postfix_to_infix(postfix);

		if (!infix)
			return -1;

		printf("%s\t\t%s\n", postfix, infix);
		free(infix);
	}

	return 0;
}
