/**
 * @file bitmasks.c
 * @brief The AKGL_BITMASK_* macros, including the ways an unparenthesized one breaks.
 *
 * These are macros, so the only thing that checks them is a call site. Most of
 * this file is the ordinary arithmetic; the composition cases at the bottom are
 * the ones that matter, because until 0.5.0 the macros expanded to bare
 * expressions and every one of them was a silent misparse waiting for a caller.
 */

#include <akgl/game.h>
#include <akgl/actor.h>

int main(void)
{
    int mask = 0;
    int counter = 0;

    AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_ALIVE);
    if ( mask != AKGL_ACTOR_STATE_ALIVE )
	return 1;
    AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_LEFT);
    if ( mask != (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_LEFT) )
	return 1;
    AKGL_BITMASK_DEL(mask, AKGL_ACTOR_STATE_ALIVE);
    if ( mask != (AKGL_ACTOR_STATE_FACE_LEFT) )
	return 1;
    AKGL_BITMASK_CLEAR(mask);
    if ( mask != 0 )
	return 1;
    AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_LEFT);
    if ( !(AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_FACE_LEFT)) )
	return 1;
    mask = AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_FACE_UP;
    AKGL_BITMASK_DEL(mask, AKGL_ACTOR_STATE_FACE_ALL);
    if ( mask != AKGL_ACTOR_STATE_ALIVE )
	return 1;
    AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_MOVING_DOWN);
    AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_FACE_DOWN);
    if ( mask != (AKGL_ACTOR_STATE_ALIVE | AKGL_ACTOR_STATE_MOVING_DOWN | AKGL_ACTOR_STATE_FACE_DOWN) )
	return 1;

    // ---- composition, which is what the parentheses are for ----

    // Negation. Unparenthesized, AKGL_BITMASK_HAS expanded to a bare
    // `(x & y) == y`, so `!` bound to the `&` and the whole thing read as
    // `!(mask & bit) == bit`.
    //
    // Note which case catches that and which does not. For a bit that *is*
    // set, `!(nonzero)` is 0 and `0 == bit` is false -- the same answer the
    // correct parse gives, so a test written that way passes either way. It
    // only diverges for a bit that is *not* set and whose value is not 1:
    // `!(0)` is 1, and `1 == 64` is false where the answer should be true.
    // That is the shape below, and it is why this was worth a real case rather
    // than an obvious one.
    mask = AKGL_ACTOR_STATE_ALIVE;
    if ( !AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_ALIVE) )
	return 1;
    if ( AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_DEAD) )
	return 1;
    if ( !AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_DEAD) ) {
	counter = 1;
    } else {
	return 1;
    }
    if ( counter != 1 )
	return 1;

    // The same divergence through AKGL_BITMASK_HASNOT, which was written the
    // same way: `!(0) != 64` is true where the answer should be false.
    if ( !AKGL_BITMASK_HASNOT(mask, AKGL_ACTOR_STATE_DEAD) )
	return 1;
    if ( AKGL_BITMASK_HASNOT(mask, AKGL_ACTOR_STATE_ALIVE) )
	return 1;

    // As an operand of a lower-precedence operator.
    if ( AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_ALIVE) == false )
	return 1;
    if ( (AKGL_BITMASK_HAS(mask, AKGL_ACTOR_STATE_ALIVE) & 1) != 1 )
	return 1;

    // The mutating macros as expressions rather than statements, and with an
    // argument that is itself an expression: `x |= y` unparenthesized cannot be
    // used in either position.
    mask = 0;
    counter = (AKGL_BITMASK_ADD(mask, AKGL_ACTOR_STATE_DYING));
    if ( counter != AKGL_ACTOR_STATE_DYING )
	return 1;
    if ( (AKGL_BITMASK_CLEAR(mask)) != 0 )
	return 1;
    if ( mask != 0 )
	return 1;

    // AKGL_BITMASK_CLEAR carries no trailing semicolon, so it is a statement
    // like any other and does not swallow the one after it when it is the whole
    // body of an unbraced `if`.
    mask = AKGL_ACTOR_STATE_ALIVE;
    counter = 0;
    if ( mask != 0 )
	AKGL_BITMASK_CLEAR(mask);
    counter = 1;
    if ( mask != 0 )
	return 1;
    if ( counter != 1 )
	return 1;

    return 0;
}
