// --------------------------------------------------------------------------
// Citadel: BoolExpr.CPP
//
// Boolean expression evaluation code stuff thingies.

#include "ctdl.h"
#pragma hdrstop

#include "boolexpr.h"


// --------------------------------------------------------------------------
// can each type be followed by...
//
//	   value|unary|open|close|binary
// val| 	|	  |    | xxx | xxx
// unr| xxx | xxx | xx |	 |
// opn| xxx | xxx | xx |	 |
// cls| 	|	  |    | xxx | xxx
// bnr| xxx | xxx | xx |	 |
//
// we initialize with an open...

enum BooleanState
	{
	BS_VALUE,	BS_UNARY,	BS_OPEN,	BS_CLOSE,	BS_BINARY
	};

Bool IsBooleanExpressionValid(const BoolExpr Expression)
	{
	BooleanState LastState = BS_OPEN;
	int NumOpen = 0;

	for (int i = 0; i < BOOL_COMPLEX; i++)
		{
		BooleanState ThisState;

		if (Expression[i] <= 32767)
			{
			ThisState = BS_VALUE;
			}
		else
			{
			switch (Expression[i])
				{
				case BOOLEAN_OPEN:
					{
					NumOpen++;
					ThisState = BS_OPEN;
					break;
					}

				case BOOLEAN_CLOSE:
					{
					if (--NumOpen < 0)
						{
						return (FALSE);
						}

					ThisState = BS_CLOSE;
					}

				case BOOLEAN_END:
					{
					ThisState = BS_CLOSE;
					break;
					}

				case BOOLEAN_AND:
				case BOOLEAN_OR:
				case BOOLEAN_XOR:
					{
					ThisState = BS_BINARY;
					break;
					}

				case BOOLEAN_NOT:
					{
					ThisState = BS_UNARY;
					break;
					}

				case BOOLEAN_TRUE:
				case BOOLEAN_FALSE:
					{
					ThisState = BS_VALUE;
					break;
					}

				default:
					{
					return (FALSE);
					}
				}
			}

		switch (LastState)
			{
			case BS_VALUE:
			case BS_CLOSE:
				{
				if (ThisState == BS_VALUE || ThisState == BS_UNARY ||
						ThisState == BS_OPEN)
					{
					return (FALSE);
					}

				break;
				}

			case BS_UNARY:
			case BS_OPEN:
			case BS_BINARY:
				{
				if (ThisState == BS_CLOSE || ThisState == BS_BINARY)
					{
					return (FALSE);
					}

				break;
				}

			default:
				{
				assert(FALSE);
				return (FALSE);
				}
			}

		if (Expression[i] == BOOLEAN_END)
			{
			return (NumOpen == 0);
			}

		LastState = ThisState;
		}

	return (FALSE);
	}

static Bool ExpressionHasGroups(const BoolExpr Expression)
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] == BOOLEAN_OPEN)
			{
			return (TRUE);
			}
		}

	return (FALSE);
	}

static int GetIndexOfOneBeyondNotInExpression(const BoolExpr Expression)
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] == BOOLEAN_NOT)
			{
			return (Index + 1);
			}
		}

	return (0);
	}

static int GetIndexOfAndInExpression(const BoolExpr Expression)
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] == BOOLEAN_AND)
			{
			return (Index);
			}
		}

	return (0);
	}

static int GetIndexOfOrInExpression(const BoolExpr Expression)
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] == BOOLEAN_OR)
			{
			return (Index);
			}
		}

	return (0);
	}

static int GetIndexOfXorInExpression(const BoolExpr Expression)
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] == BOOLEAN_XOR)
			{
			return (Index);
			}
		}

	return (0);
	}

static Bool TestValue(uint Code, Bool (*Tester)(int))
	{
	if (Code <= 32767)
		{
		return ((*Tester)(Code));
		}

	if (Code == BOOLEAN_TRUE)
		{
		return (TRUE);
		}

	// BOOLEAN_FALSE or bad value
	return (FALSE);
	}

static void ExtractInnerGroup(BoolExpr Original, BoolExpr Extraction, int *Place)
	{
	int Index, LastFound = -1;

	for (Index = 0; Index < BOOL_COMPLEX && Original[Index] != BOOLEAN_END;
			Index++)
		{
		if (Original[Index] == BOOLEAN_OPEN)
			{
			LastFound = Index;
			}
		}

	assert(LastFound != -1);

	for (Index = LastFound + 1;
			Index < BOOL_COMPLEX && Original[Index] != BOOLEAN_CLOSE; Index++)
		{
		Extraction[Index - LastFound - 1] = Original[Index];
		}

	Extraction[Index - LastFound - 1] = BOOLEAN_END;

	memcpy(Original + LastFound, Original + Index, BOOL_COMPLEX - Index - 1);

	*Place = LastFound;
	}

#ifndef NDEBUG
static void ShowInt(int Wow)
	{
	mPrintf(pctd, Wow);
	}
#endif

Bool EvaluateBooleanExpression(const BoolExpr InpExpression, Bool (*Tester)(int))
	{
	DEBUGCODE(if (debug) {ShowBooleanExpression(InpExpression, ShowInt); doCR();});

	if (!IsBooleanExpressionValid(InpExpression))
		{
		return (FALSE);
		}

	BoolExpr Expression;

	memcpy(Expression, InpExpression, sizeof(BoolExpr));

	// evaluate groups first
	while (ExpressionHasGroups(Expression))
		{
		BoolExpr ExtractedGroup;
		int Index;

		ExtractInnerGroup(Expression, ExtractedGroup, &Index);

		Expression[Index] =
				EvaluateBooleanExpression(ExtractedGroup, Tester) ?
				BOOLEAN_TRUE : BOOLEAN_FALSE;

		DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
		}

	// then "not"s
	while (GetIndexOfOneBeyondNotInExpression(Expression))
		{
		int Index = GetIndexOfOneBeyondNotInExpression(Expression);
		Bool Value = TestValue(Expression[Index], Tester);

		memcpy(Expression + Index - 1, Expression + Index, sizeof(uint) * (BOOL_COMPLEX - Index));

		Expression[Index - 1] = Value ? BOOLEAN_FALSE : BOOLEAN_TRUE;

		DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
		}

	// then "and"s
	while (GetIndexOfAndInExpression(Expression))
		{
		int Index = GetIndexOfAndInExpression(Expression);

		Bool Value1 = TestValue(Expression[Index - 1], Tester);
		Bool Value2 = TestValue(Expression[Index + 1], Tester);

		memcpy(Expression + Index - 1, Expression + Index + 1,
				sizeof(uint) * (BOOL_COMPLEX - Index - 1));

		Expression[Index - 1] = (Value1 && Value2) ?
				BOOLEAN_TRUE : BOOLEAN_FALSE;

		DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
		}

	// then "or"s
	while (GetIndexOfOrInExpression(Expression))
		{
		int Index = GetIndexOfOrInExpression(Expression);

		Bool Value1 = TestValue(Expression[Index - 1], Tester);
		Bool Value2 = TestValue(Expression[Index + 1], Tester);

		memcpy(Expression + Index - 1, Expression + Index + 1,
				sizeof(uint) * (BOOL_COMPLEX - Index - 1));

		Expression[Index - 1] = (Value1 || Value2) ?
				BOOLEAN_TRUE : BOOLEAN_FALSE;

		DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
		}

	// then "xor"s
	while (GetIndexOfXorInExpression(Expression))
		{
		int Index = GetIndexOfXorInExpression(Expression);

		Bool Value1 = TestValue(Expression[Index - 1], Tester);
		Bool Value2 = TestValue(Expression[Index + 1], Tester);

		memcpy(Expression + Index - 1, Expression + Index + 1,
				sizeof(uint) * (BOOL_COMPLEX - Index - 1));

		Expression[Index - 1] = (Value1 ^ Value2) ?
				BOOLEAN_TRUE : BOOLEAN_FALSE;

		DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
		}

	DEBUGCODE(if (debug) {ShowBooleanExpression(Expression, ShowInt); doCR();});
	return (TestValue(Expression[0], Tester));
	}

void ShowBooleanExpression(const BoolExpr Expression, void (*Shower)(int))
	{
	int Index;

	for (Index = 0; Index < BOOL_COMPLEX && Expression[Index] != BOOLEAN_END;
			Index++)
		{
		if (Expression[Index] <= 32767)
			{
			(*Shower)(Expression[Index]);
			}
		else
			{
			switch (Expression[Index])
				{
				case BOOLEAN_OPEN:		mPrintf(getmsg(1421));	break;
				case BOOLEAN_CLOSE: 	mPrintf(getmsg(1422));	break;
				case BOOLEAN_AND:		mPrintf(getmsg(1423));	break;
				case BOOLEAN_OR:		mPrintf(getmsg(1424));	break;
				case BOOLEAN_NOT:		mPrintf(getmsg(1425));	break;
				case BOOLEAN_XOR:		mPrintf(getmsg(1426));	break;
				case BOOLEAN_TRUE:		mPrintf(getmsg(896));	break;
				case BOOLEAN_FALSE: 	mPrintf(getmsg(895));	break;
				default:				mPrintf(getmsg(1427));	break;
				}
			}

		mPrintf(spc);
		}
	}

Bool CreateBooleanExpression(char *InputString, int (*Tester)(const char *), BoolExpr Expression)
	{
	char *words[256];
	int count;

	count = parse_it(words, InputString);

	if (count <= BOOL_COMPLEX)
		{
		int i;

		for (i = 0; i < count; i++)
			{
			if (SameString(words[i], getmsg(1421)))
				{
				Expression[i] = BOOLEAN_OPEN;
				}
			else if (SameString(words[i], getmsg(1422)))
				{
				Expression[i] = BOOLEAN_CLOSE;
				}
			else if (SameString(words[i], getmsg(1423)))
				{
				Expression[i] = BOOLEAN_AND;
				}
			else if (SameString(words[i], getmsg(1424)))
				{
				Expression[i] = BOOLEAN_OR;
				}
			else if (SameString(words[i], getmsg(1425)))
				{
				Expression[i] = BOOLEAN_NOT;
				}
			else if (SameString(words[i], getmsg(1426)))
				{
				Expression[i] = BOOLEAN_XOR;
				}
			else if (SameString(words[i], getmsg(896)))
				{
				Expression[i] = BOOLEAN_TRUE;
				}
			else if (SameString(words[i], getmsg(895)))
				{
				Expression[i] = BOOLEAN_FALSE;
				}
			else
				{
				int Value = (*Tester)(words[i]);

				if (Value != CERROR)
					{
					Expression[i] = Value;
					}
				else
					{
					return (FALSE);
					}
				}
			}

		Expression[i] = BOOLEAN_END;

		if (IsBooleanExpressionValid(Expression))
			{
			return (TRUE);
			}
		}

	return (FALSE);
	}

Bool IsInBooleanExpression(const BoolExpr Expression, uint ToTest)
	{
	for (int i = 0; i < BOOL_COMPLEX; i++)
		{
		if (Expression[i] == ToTest)
			{
			return (TRUE);
			}

		if (Expression[i] == BOOLEAN_END)
			{
			break;
			}
		}

	return (FALSE);
	}
