#pragma once

#include "log.qh"

/// Only ever assign into the first 24 bits in QC (so max is BIT(23)).
/// QC converts the float to int, performs the bit operation, then converts it back.
/// Assigning to the highest bits means some of the low ones might get lost due to float precision.
#define BIT(n) (1 << (n))
#define BITS(n) (BIT(n) - 1)
#ifndef BRANCHLESS_BITSET
	#define BITSET(var, mask, flag) (flag ? (var) | (mask) : (var) & ~(mask))
#else
	#define BITSET(var, mask, flag) ((var) ^ (-(flag) ^ (var)) & (mask))
#endif

ERASEABLE
int lowestbit(int f)
{
	f &= ~(f << 1);
	f &= ~(f << 2);
	f &= ~(f << 4);
	f &= ~(f << 8);
	f &= ~(f << 16);
	return f;
}

ERASEABLE
int randombit(int bits)
{
	if (!(bits & (bits - 1))) // this ONLY holds for powers of two!
		return bits;

	int r = random();
	int b = 0;
	int n = 0;
	for (int f = 1; f <= bits; f *= 2)
		if (bits & f)
		{
			++n;
			r *= n;
			if (r <= 1)
				b = f;
			else
				r = (r - 1) / (n - 1);
		}

	return b;
}

ERASEABLE
int randombits(int bits, int k, bool error_return)
{
	int r = 0;
	while (k > 0 && bits != r)
	{
		r += randombit(bits - r);
		--k;
	}
	if (error_return && k > 0)
		return -1;
	// all
	return r;
}

/*
void randombit_test(int bits, int iter)
{
	while (iter > 0)
	{
		LOG_INFO(ftos(randombit(bits)), "\n");
		--iter;
	}
}
*/

enum
{
	OP_SET,
	OP_MIN,
	OP_MAX,
	OP_PLUS,
	OP_MINUS
};

ERASEABLE
bool GiveBit(entity e, .int fld, int bit, int op, int val)
{
	int v0 = (e.(fld) & bit);
	switch (op)
	{
		case OP_SET:
			if (val > 0)
				e.(fld) |= bit;
			else
				e.(fld) &= ~bit;
			break;
		case OP_MIN:
		case OP_PLUS:
			if (val > 0)
				e.(fld) |= bit;
			break;
		case OP_MAX:
			if (val <= 0)
				e.(fld) &= ~bit;
			break;
		case OP_MINUS:
			if (val > 0)
				e.(fld) &= ~bit;
			break;
	}
	int v1 = (e.(fld) & bit);
	return v0 != v1;
}

ERASEABLE
bool GiveValue(entity e, .int fld, int op, int val)
{
	int v0 = e.(fld);
	switch (op)
	{
		case OP_SET:
			e.(fld) = val;
			break;
		case OP_MIN:
			e.(fld) = max(e.(fld), val);  // min 100 cells = at least 100 cells
			break;
		case OP_MAX:
			e.(fld) = min(e.(fld), val);
			break;
		case OP_PLUS:
			e.(fld) += val;
			break;
		case OP_MINUS:
			e.(fld) -= val;
			break;
	}
	int v1 = e.(fld);
	return v0 != v1;
}
