/*
=================
PC_Expression_Parse
=================
*/
static bool PC_Expression_Parse( int handle, float *f )
{
	pc_token_t  token;
	int         unmatchedParentheses = 0;
	exprList_t  stack, fifo;
	exprToken_t *value;
	bool    expectingNumber = true;

#define FULL( a )  ( a.b >= ( MAX_EXPR_ELEMENTS - 1 ) )
#define EMPTY( a ) ( a.f > a.b )

#define PUSH_VAL( a, v ) \
  { \
    if( FULL( a ) ) { \
      return false; } \
    a.b++; \
    a.l[ a.b ].type = EXPR_VALUE; \
    a.l[ a.b ].u.val = v; \
  }

#define PUSH_OP( a, o ) \
  { \
    if( FULL( a ) ) { \
      return false; } \
    a.b++; \
    a.l[ a.b ].type = EXPR_OPERATOR; \
    a.l[ a.b ].u.op = o; \
  }

#define POP_STACK( a ) \
  { \
    if( EMPTY( a ) ) { \
      return false; } \
    value = &a.l[ a.b ]; \
    a.b--; \
  }

#define PEEK_STACK_OP( a )  ( a.l[ a.b ].u.op )
#define PEEK_STACK_VAL( a ) ( a.l[ a.b ].u.val )

#define POP_FIFO( a ) \
  { \
    if( EMPTY( a ) ) { \
      return false; } \
    value = &a.l[ a.f ]; \
    a.f++; \
  }

	stack.f = fifo.f = 0;
	stack.b = fifo.b = -1;

	while ( trap_Parse_ReadToken( handle, &token ) )
	{
		if ( !unmatchedParentheses && token.string[ 0 ] == ')' )
		{
			break;
		}

		// Special case to catch negative numbers
		if ( expectingNumber && token.string[ 0 ] == '-' )
		{
			if ( !trap_Parse_ReadToken( handle, &token ) )
			{
				return false;
			}

			token.floatvalue = -token.floatvalue;
		}

		if ( token.type == TT_NUMBER )
		{
			if ( !expectingNumber )
			{
				return false;
			}

			expectingNumber = !expectingNumber;

			PUSH_VAL( fifo, token.floatvalue );
		}
		else
		{
			switch ( token.string[ 0 ] )
			{
				case '(':
					unmatchedParentheses++;
					PUSH_OP( stack, '(' );
					break;

				case ')':
					unmatchedParentheses--;

					if ( unmatchedParentheses < 0 )
					{
						return false;
					}

					while ( !EMPTY( stack ) && PEEK_STACK_OP( stack ) != '(' )
					{
						POP_STACK( stack );
						PUSH_OP( fifo, value->u.op );
					}

					// Pop the '('
					POP_STACK( stack );

					break;

				case '*':
				case '/':
				case '+':
				case '-':
					if ( expectingNumber )
					{
						return false;
					}

					expectingNumber = !expectingNumber;

					if ( EMPTY( stack ) )
					{
						PUSH_OP( stack, token.string[ 0 ] );
					}
					else
					{
						while ( !EMPTY( stack ) && OpPrec( token.string[ 0 ] ) < OpPrec( PEEK_STACK_OP( stack ) ) )
						{
							POP_STACK( stack );
							PUSH_OP( fifo, value->u.op );
						}

						PUSH_OP( stack, token.string[ 0 ] );
					}

					break;

				default:
					// Unknown token
					return false;
			}
		}
	}

	while ( !EMPTY( stack ) )
	{
		POP_STACK( stack );
		PUSH_OP( fifo, value->u.op );
	}

	while ( !EMPTY( fifo ) )
	{
		POP_FIFO( fifo );

		if ( value->type == EXPR_VALUE )
		{
			PUSH_VAL( stack, value->u.val );
		}
		else if ( value->type == EXPR_OPERATOR )
		{
			char  op = value->u.op;
			float operand1, operand2, result;

			POP_STACK( stack );
			operand2 = value->u.val;
			POP_STACK( stack );
			operand1 = value->u.val;

			switch ( op )
			{
				case '*':
					result = operand1 * operand2;
					break;

				case '/':
					result = operand1 / operand2;
					break;

				case '+':
					result = operand1 + operand2;
					break;

				case '-':
					result = operand1 - operand2;
					break;

				default:
					Com_Error( ERR_FATAL, "Unknown operator '%c' in postfix string", op );
			}

			PUSH_VAL( stack, result );
		}
	}

	POP_STACK( stack );

	*f = value->u.val;

	return true;

#undef FULL
#undef EMPTY
#undef PUSH_VAL
#undef PUSH_OP
#undef POP_STACK
#undef PEEK_STACK_OP
#undef PEEK_STACK_VAL
#undef POP_FIFO
}
static Expr* WFS_ExprBuildInternal(char*** ppapszTokens,
                                   ExprBuildContext* psBuildContext)
{
    Expr* expr = NULL;
    Expr* op = NULL;
    Expr* val1 = NULL;
    Expr* val2 = NULL;
    CPLList* psValExprList = NULL;
    CPLList* psOpExprList = NULL;
    char** papszTokens = *ppapszTokens;
    char* pszToken = NULL;

#define PEEK_OP(my_op) my_op = (Expr*)CPLListGetData(psOpExprList)
#define PUSH_OP(my_op) psOpExprList = CPLListInsert(psOpExprList, my_op, 0)
#define POP_OP(my_op) do { my_op = (Expr*)CPLListGetData(psOpExprList);  \
                           if (my_op != NULL) { \
                                CPLList* psNext = psOpExprList->psNext; \
                                CPLFree(psOpExprList); \
                                psOpExprList = psNext; \
                           } \
                      } while(0)
#define PUSH_VAL(my_val) psValExprList = CPLListInsert(psValExprList, my_val, 0)
#define POP_VAL(my_val) do { my_val = (Expr*)CPLListGetData(psValExprList); \
                           if (my_val != NULL) { \
                                CPLList* psNext = psValExprList->psNext; \
                                CPLFree(psValExprList); \
                                psValExprList = psNext; \
                           } \
                      } while(0)
    while(TRUE)
    {
        pszToken = *papszTokens;
        if (pszToken == NULL)
            break;
        papszTokens ++;

        if (EQUAL(pszToken, "("))
        {
            char** papszSub = papszTokens;
            psBuildContext->nParenthesisLevel ++;
            Expr* expr = WFS_ExprBuildInternal(&papszSub, psBuildContext);
            psBuildContext->nParenthesisLevel --;
            if (expr == NULL)
                goto invalid_expr;
            PUSH_VAL(expr);
            papszTokens = papszSub;
            if (*papszTokens == NULL)
                break;

            continue;
        }
        else if (EQUAL(pszToken, ")"))
        {
            if (psBuildContext->nParenthesisLevel > 0)
                break;
            else
                goto invalid_expr;
        }

        if (psBuildContext->bExpectVarName)
        {
            if (EQUAL(pszToken, "NOT"))
                op = WFS_ExprBuildOperator(TOKEN_NOT);
            else
            {
                PUSH_VAL(WFS_ExprBuildVarName(pszToken));
                psBuildContext->bExpectVarName = FALSE;
                psBuildContext->bExpectComparisonOperator = TRUE;
            }
        }
        else if (psBuildContext->bExpectComparisonOperator)
        {
            psBuildContext->bExpectComparisonOperator = FALSE;
            psBuildContext->bExpectValue = TRUE;
            if (EQUAL(pszToken, "IS"))
            {
                if (*papszTokens != NULL && EQUAL(*papszTokens, "NOT"))
                {
                    op = WFS_ExprBuildOperator(TOKEN_NOT_EQUAL);
                    papszTokens ++;
                }
                else
                    op = WFS_ExprBuildOperator(TOKEN_EQUAL);
            }
            else if (EQUAL(pszToken, "="))
                op = WFS_ExprBuildOperator(TOKEN_EQUAL);
            else if (EQUAL(pszToken, "LIKE") || EQUAL(pszToken, "ILIKE"))
                op = WFS_ExprBuildOperator(TOKEN_LIKE);
            else if (EQUAL(pszToken, "!=") || EQUAL(pszToken, "<>"))
                op = WFS_ExprBuildOperator(TOKEN_NOT_EQUAL);
            else if (EQUAL(pszToken, "<"))
                op = WFS_ExprBuildOperator(TOKEN_LESSER);
            else if (EQUAL(pszToken, "<="))
                op = WFS_ExprBuildOperator(TOKEN_LESSER_OR_EQUAL);
            else if (EQUAL(pszToken, ">"))
                op = WFS_ExprBuildOperator(TOKEN_GREATER);
            else if (EQUAL(pszToken, ">="))
                op = WFS_ExprBuildOperator(TOKEN_GREATER_OR_EQUAL);
            else
                goto invalid_expr;
        }
        else if (psBuildContext->bExpectLogicalOperator)
        {
            psBuildContext->bExpectLogicalOperator = FALSE;
            psBuildContext->bExpectVarName = TRUE;
            if (EQUAL(pszToken, "AND"))
                op = WFS_ExprBuildOperator(TOKEN_AND);
            else if (EQUAL(pszToken, "OR"))
                op = WFS_ExprBuildOperator(TOKEN_OR);
            else if (EQUAL(pszToken, "NOT"))
                op = WFS_ExprBuildOperator(TOKEN_NOT);
            else
                goto invalid_expr;
        }
        else if (psBuildContext->bExpectValue)
        {
            PUSH_VAL(WFS_ExprBuildValue(pszToken));
            psBuildContext->bExpectValue = FALSE;
            psBuildContext->bExpectLogicalOperator = TRUE;
        }
        else
            goto invalid_expr;

        if (op != NULL)
        {
            Expr* prevOp;

            while(TRUE)
            {
                PEEK_OP(prevOp);

                if (prevOp != NULL &&
                        (WFS_ExprGetPriority(op) <= WFS_ExprGetPriority(prevOp)))
                {
                    if (prevOp->eType != TOKEN_NOT)
                    {
                        POP_VAL(val2);
                        if (val2 == NULL) goto invalid_expr;
                    }
                    POP_VAL(val1);
                    if (val1 == NULL) goto invalid_expr;

                    PUSH_VAL(WFS_ExprBuildBinary(prevOp->eType, val1, val2));
                    POP_OP(prevOp);
                    WFS_ExprFree(prevOp);
                    val1 = val2 = NULL;
                }
                else
                    break;
            }

            PUSH_OP(op);
            op = NULL;
        }

    }

    *ppapszTokens = papszTokens;

    while(TRUE)
    {
        POP_OP(op);
        if (op == NULL)
            break;
        if (op->eType != TOKEN_NOT)
        {
            POP_VAL(val2);
            if (val2 == NULL) goto invalid_expr;
        }
        POP_VAL(val1);
        if (val1 == NULL) goto invalid_expr;
        PUSH_VAL(WFS_ExprBuildBinary(op->eType, val1, val2));
        val1 = val2 = NULL;

        WFS_ExprFree(op);
        op = NULL;
    }

    POP_VAL(expr);
    return expr;

invalid_expr:
    WFS_ExprFree(op);
    WFS_ExprFree(val1);
    WFS_ExprFree(val2);
    WFS_ExprFreeList(psValExprList);
    WFS_ExprFreeList(psOpExprList);

    return NULL;
}