/*******************************************************************************\
|* This code was written by Kirk Johnson (ammaross@crosswinds.net) to          *|
|* experiment with flags outside of the mud program. Within this code are bits *|
|* and pieces (ie fread_string) from the ROM codebase (written by Russ Taylor. *|
|* This program was also written to experiment with Edwin Groothuis's bit      *|
|* system. Thanks guys! This code is freely distrubutable as long as this      *|
|* header remains intact.                                                      *|
|*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*|
|*    _____   ____     ____     _____  __    _  __ _     __     ____     _____ *|
|*   /@/   ) /@/  )   /@/  \   /@/    /@ \   / /@ V \   /@ \   /@/  )   /@/    *|
|*  /@/ --/ /@/ -<   /@/    | /@/--  /@/\ \ / /@/\_/ \ (@(  ) /@/ -<   /@/--   *|
|* (@(   ( (@(    \ (@(____/ (@(___ (@(  \_/ (@(     _\ \@_/ (@(    \ (@(___   *|
|*                                                                             *|
\*******************************************************************************/
#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

typedef int		FLAG;
#define FLAG_BITSIZE	16
#define MAX_FLAGS	3  // How big the char string is ( MAX_FLAGS*16 == #of bits possible )

// Old bit defines
#define A		  	1
#define B			2
#define C			4
#define D			8
#define E			16
#define F			32
#define G			64
#define H			128

#define I			256
#define J			512
#define K		    1024
#define L		 	2048
#define M			4096
#define N		 	8192
#define O			16384
#define P			32768

#define Q			65536
#define R			131072
#define S			262144
#define T			524288
#define U			1048576
#define V			2097152
#define W			4194304
#define X			8388608

#define Y			16777216
#define Z			33554432
#define aa			67108864 	/* doubled due to conflicts */
#define bb			134217728
#define cc			268435456
#define dd			536870912
#define ee			1073741824

// Sample new bit defines
#define STR_A		0
#define STR_B		1
#define STR_C		2
#define STR_D		3
#define STR_E		4
#define STR_F		5
#define STR_G		6
#define STR_H		7

#define STR_I		8
#define STR_J		9
#define STR_K		10
#define STR_L		11
#define STR_M		12
#define STR_N		13
#define STR_O		14
#define STR_P		15

#define STR_Q		16
#define STR_R		17
#define STR_S		18
#define STR_T		19
#define STR_U		20
#define STR_V		21
#define STR_W		22
#define STR_X		23

#define STR_Y		24
#define STR_Z		25
#define STR_aa		26
#define STR_bb		27
#define STR_cc		28
#define STR_dd		29
#define STR_ee		30

#define STR_ff		31
#define STR_gg		32

// New bit macros
#define STR_ZERO_STR(var,size)  { int i; for(i=0;i<(size);i++){(var)[i]=0;}}
#define STR_SAME_STR(a,b,size)  (memcmp((a),(b),(size))==0)
#define STR_COPY_STR(a,b,size)	{ int i;for(i=0;i<(size);i++){(a)[i]= (b)[i];} }
#define STR_AND_STR(a,b,size)   { int i;for(i=0;i<(size);i++){(a)[i]&=(b)[i];}}
#define STR_OR_STR(a,b,size)    { int i;for(i=0;i<(size);i++){(a)[i]|=(b)[i];}}
#define STR_XOR_STR(a,b,size)   { int i;for(i=0;i<(size);i++){(a)[i]^=(b)[i];}}
#define STR_NOT_STR(a,size)     { int i;for(i=0;i<(size);i++){(a)[i]=~((a)[i]);}}
#define STR_REMOVE_STR(a,b,size){ int i;for(i=0;i<(size);i++){(a)[i]&=~((b)[i]);}}
bool STR_IS_ZERO(FLAG*var,int size);
bool STR_AND(FLAG*var1,FLAG*var2,int size);
FLAG *DIF( FLAG *var1, FLAG *var2, FLAG *buf, int size );
#define STR_IS_SET(var,bit)     ((((FLAG *)(var))[((bit)/FLAG_BITSIZE)]) &   ((1<<((bit)%FLAG_BITSIZE))))
#define STR_SET_BIT(var,bit)    ((((FLAG *)(var))[((bit)/FLAG_BITSIZE)]) |=  ((1<<((bit)%FLAG_BITSIZE))))
#define STR_REMOVE_BIT(var,bit) ((((FLAG *)(var))[((bit)/FLAG_BITSIZE)]) &= ~((1<<((bit)%FLAG_BITSIZE))))
#define STR_TOGGLE_BIT(var,bit) ((((FLAG *)(var))[((bit)/FLAG_BITSIZE)]) ^=  ((1<<((bit)%FLAG_BITSIZE))))

// Old bit macros
#define IS_SET(flag, bit)	((flag) & (bit))
#define SET_BIT(var, bit)	((var) |= (bit))
#define REMOVE_BIT(var, bit)	((var) &= ~(bit))
#define TOGGLE_BIT(var, bit)    ((var) ^= (bit))

// Function declarations
// old bit testing
void long_bit_test( void );
void print_flags( long flags );
void print_results( long flags1, long flags2 );
int bit_value( char letter );
// new bit testing
void str_bit_test( void );
void str_print_flags_screen( FLAG *flags );
void str_print_results( FLAG *flags1, FLAG *flags2 );
void str_writefile( FLAG *flags1, FLAG *flags2 );
void str_readfile_print( void );
int fread_number( FILE *fp );
FLAG *str_fread_flag( FILE *fp, const int size );
char *str_print_flags( FLAG *flags, const int size );
char *str_fwrite_flag( FLAG *flags, const int size, char *out );

// Main Functions
int main( void )
{
int request;

	cout << "Available test runs:\n";
	cout << "  1 - Bits using long int\n";
	cout << "  2 - Bits using strings\n";
	cout << '\n';
	while (1)
	{
		cout << "Enter a request: ";

		cin >> request;
		cin.get(); // Pick up the '\r'
		if ( request == 1 ) {
			long_bit_test();
			break;
		}
		else if ( request == 2 ) {
			str_bit_test();
			break;
		}
		else {
			cout << "Invalid request! Try again.\n";
		}
	}
	return 0;
}

void long_bit_test( void )
{
	long flags1;
	long flags2;
	long a;

	cout << "Bit test with long ints:\n\n";
	flags1 = 0;
	flags2 = flags1;
	a = 0;

	cout << "Zeroed out:\n";
	cout << "flags1: "; print_flags( flags1 );
	cout << "flags2: "; print_flags( flags2 );
	cout << '\n';

	SET_BIT( flags1, C );
	SET_BIT( flags2, D );

	cout << "With flags1: C. flags2: D\n";
	print_results( flags1, flags2 );
	cout << '\n';
	SET_BIT( flags1, F );
	SET_BIT( flags2, F );
	cout << "With flags1: CF. flags2: DF\n";
	print_results( flags1, flags2 ); // Test with 2 flags set. Interesting... :)
	cout << '\n';
	cout << "Bit value D: ";
		SET_BIT( a, D );
		print_flags( a );
		a = 0;
	cout << "Bit value A: ";
		SET_BIT( a, A );
		print_flags( a );
		a = 0;
	cout << "Bit value d: ";
		SET_BIT( a, dd );
		print_flags( a );
		a = 0;
	cout << '\n';
	cout << "[Hit return to quit] ";
	cin.get();
	return;
}

void str_bit_test( void )
{
	FLAG flags1[MAX_FLAGS];
	FLAG flags2[MAX_FLAGS];
	FLAG a[MAX_FLAGS];

	cout << "Bit test with strings:\n\n";
	STR_ZERO_STR( flags1, MAX_FLAGS );
	STR_ZERO_STR( flags2, MAX_FLAGS );
	STR_ZERO_STR( a, MAX_FLAGS );

	cout << "Zeroed out:\n";
	cout << "flags1: "; str_print_flags_screen( flags1 );
	cout << "flags2: "; str_print_flags_screen( flags2 );
	cout << '\n';

	STR_SET_BIT( flags1, STR_C );
	STR_SET_BIT( flags2, STR_D );

	cout << "With flags1: C. flags2: D\n";
	str_print_results( flags1, flags2 );
	cout << '\n';
	STR_SET_BIT( flags1, STR_F );
	STR_SET_BIT( flags2, STR_F );
	cout << "With flags1: CF. flags2: DF\n";
	
	str_print_results( flags1, flags2 );

	cout << '\n';
	cout << "Bit value D:  ";
		STR_SET_BIT( a, STR_D );
		str_print_flags_screen( a );
		STR_ZERO_STR( a, MAX_FLAGS );
	cout << "Bit value A:  ";
		STR_SET_BIT( a, STR_A );
		str_print_flags_screen( a );
		STR_ZERO_STR( a, MAX_FLAGS );
	cout << "Bit value dd: ";
		STR_SET_BIT( a, STR_dd );
		str_print_flags_screen( a );
		STR_ZERO_STR( a, MAX_FLAGS );
	cout << '\n';

	STR_ZERO_STR( flags1, MAX_FLAGS );
	STR_ZERO_STR( flags2, MAX_FLAGS );

	STR_SET_BIT( flags1, STR_B );
	STR_SET_BIT( flags1, STR_C );
	STR_SET_BIT( flags1, STR_F );
	STR_SET_BIT( flags1, STR_ff );
	STR_SET_BIT( flags2, STR_A );
	STR_SET_BIT( flags2, STR_D );
	STR_SET_BIT( flags2, STR_F );
	STR_SET_BIT( flags2, STR_ee );
	STR_SET_BIT( flags2, STR_gg );

	str_writefile( flags1, flags2 );
	cout << "Flags1: "; str_print_flags_screen( flags1 );
	cout << "Flags2: "; str_print_flags_screen( flags2 );
	str_readfile_print();
	STR_REMOVE_STR( flags1, flags2, MAX_FLAGS);
//	STR_NOT_STR( flags1, MAX_FLAGS );
//	STR_AND_STR( flags1, flags2, MAX_FLAGS );
//	STR_OR_STR( flags1, flags2, MAX_FLAGS );
	cout << "flags1: ";
	str_print_flags_screen( flags1 );
	cout << "[Hit return to quit] ";
	cin.get();
	return;
}

void print_results( long flags1, long flags2 )
{
	cout << "flags1: ";
		print_flags( flags1 );
	cout << "flags2: ";
		print_flags( flags2 );

	// This does the specificed action (but without assigning)
	cout << "flags1 &  flags2: "; print_flags( flags1 & flags2 ); // IS_SET
	cout << "flags1 |  flags2: "; print_flags( flags1 | flags2 ); // SET_BIT
	cout << "flags1 ^  flags2: "; print_flags( flags1 ^ flags2 ); // TOGGLE_BIT
	cout << "flags1 & ~flags2: "; print_flags( flags1 & ~flags2 ); // REMOVE_BIT
	// Just additional things I decided to try. (inverses flags2 then set/toggle)
	cout << "flags1 | ~flags2: "; print_flags( flags1 | ~flags2 );
	cout << "flags1 ^ ~flags2: "; print_flags( flags1 ^ ~flags2 );

	return;
}

void str_print_results( FLAG *flags1, FLAG *flags2 )
{
	cout << "flags1: ";
		str_print_flags_screen( flags1 );
	cout << "flags2: ";
		str_print_flags_screen( flags2 );

	cout << "flags1 IS_SET D     : " << (STR_IS_SET(flags1, STR_D ) ? "Yes" : "No") << '\n';
	STR_TOGGLE_BIT(flags1, STR_D );
	cout << "flags1 TOGGLE_BIT D : "; str_print_flags_screen( flags1 );
	cout << "flags1 IS_SET D     : " << (STR_IS_SET(flags1, STR_D ) ? "Yes" : "No") << '\n';
	STR_REMOVE_BIT(flags1, STR_D );
	cout << "flags1 REMOVE_BIT D : "; str_print_flags_screen( flags1 );
	cout << "flags1 IS_SET D     : " << (STR_IS_SET(flags1, STR_D ) ? "Yes" : "No") << '\n';

	cout << "flags2 IS_SET ee    : " << (STR_IS_SET(flags2, STR_ee ) ? "Yes" : "No") << '\n';
	STR_TOGGLE_BIT(flags2, STR_ee );
	cout << "flags2 TOGGLE_BIT ee: "; str_print_flags_screen( flags2 );
	cout << "flags2 IS_SET ee    : " << (STR_IS_SET(flags2, STR_ee ) ? "Yes" : "No") << '\n';
	STR_REMOVE_BIT(flags2, STR_ee );
	cout << "flags2 REMOVE_BIT ee: "; str_print_flags_screen( flags2 );
	cout << "flags2 IS_SET ee    : " << (STR_IS_SET(flags2, STR_ee ) ? "Yes" : "No") << '\n';

	return;
}

void print_flags( long flags )
{
long int i;

	for ( i = 1; i <= ee; i *= 2 )
	{
		IS_SET( flags, i ) ? cout << '1' : cout << '0';
		if ( i == ee ) // Bail out just incase something does go screwy when
			break;     // i exceeds MAX_LONG_INT
	}
	cout << '\n';

	return;
}

void str_print_flags_screen( FLAG *flags )
{
int max = MAX_FLAGS*FLAG_BITSIZE;
int i;

	for ( i = 0; i < max; i++ )
	{
		STR_IS_SET( flags, i ) ? cout << '1' : cout << '0';
	}
	cout << '\n';

	return;
}

int bit_value( char letter )
{ /* *sigh*. Anyone have a better way to do this? */
	switch (letter)
	{
	case 'A':	return A;
	case 'B':	return B;
	case 'C':	return C;
	case 'D':	return D;
	case 'E':	return E;
	case 'F':	return F;
	case 'G':	return G;
	case 'H':	return H;
	case 'I':	return I;
	case 'J':	return J;
	case 'K':	return K;
	case 'L':	return L;
	case 'M':	return M;
	case 'N':	return N;
	case 'O':	return O;
	case 'P':	return P;
	case 'Q':	return Q;
	case 'R':	return R;
	case 'S':	return S;
	case 'T':	return T;
	case 'U':	return U;
	case 'V':	return V;
	case 'W':	return W;
	case 'X':	return X;
	case 'Y':	return Y;
	case 'Z':	return Z;
	case 'a':	return aa;
	case 'b':	return bb;
	case 'c':	return cc;
	case 'd':	return dd;
	case 'e':	return ee;
	default: 	return 0;
	}

	return 0;
}

void str_writefile( FLAG *flags1, FLAG *flags2 )
{
	FILE *fp;
	char *filename;
	char buf[1024];

	filename = strdup( "strflags.txt" );
	if ( ( fp = fopen( filename, "w" ) ) == NULL )
	{
		cout << "Error: Cound not write to " << filename << '\n';
		return;
	}

	fprintf( fp, "%s\n", str_fwrite_flag(flags1,MAX_FLAGS,buf) );
	fprintf( fp, "%s\n", str_fwrite_flag(flags2,MAX_FLAGS,buf) );
	fprintf( fp, "\n\n" );
	
	fclose( fp );
	return;
}

void str_readfile_print( void )
{
	FILE *fp;
	char *filename;
	FLAG flags1[MAX_FLAGS];
	FLAG flags2[MAX_FLAGS];
	int i;

	STR_ZERO_STR(flags1,MAX_FLAGS);
	STR_ZERO_STR(flags2,MAX_FLAGS);

	filename = strdup( "strflags.txt" );
	if ( ( fp = fopen( filename, "r" ) ) == NULL )
	{
		cout << "Error: Could not open " << filename << '\n';
		return;
	}

	STR_COPY_STR( flags1, str_fread_flag( fp, MAX_FLAGS ), MAX_FLAGS );
	STR_COPY_STR( flags2, str_fread_flag( fp, MAX_FLAGS ), MAX_FLAGS );

	cout << "Read from File: ";
	for ( i = 0; i < MAX_FLAGS; i++ )
		cout << flags1[i] << ' ';
	cout << '\n';
	cout << "Read from File: ";
	for ( i = 0; i < MAX_FLAGS; i++ )
		cout << flags2[i] << ' ';
	cout << '\n';

	cout << "Read flag1: ";
    str_print_flags_screen( flags1 );
    cout << "Read flag2: ";
    str_print_flags_screen( flags2 );

	fclose(fp);
    return;
}

/*
 * Read a number from a file.
 */
int fread_number( FILE *fp )
{
    int number;
    bool sign;
    char c;

    do
    {
	c = getc( fp );
    }
    while ( isspace(c) );

    number = 0;

    sign   = false;
    if ( c == '+' )
    {
	c = getc( fp );
    }
    else if ( c == '-' )
    {
	sign = true;
	c = getc( fp );
    }

    if ( !isdigit(c) )
    {
		cout << "Fread_number: bad format.";
		exit( 1 );
    }

    while ( isdigit(c) )
    {
	number = number * 10 + c - '0';
	c      = getc( fp );
    }

    if ( sign )
	number = 0 - number;

    if ( c == '|' )
	number += fread_number( fp );
    else if ( c != ' ' )
	ungetc( c, fp );

    return number;
}

long flag_convert(char letter )
{
    long bitsum = 0;
    char i;

    if ('A' <= letter && letter <= 'Z') 
    {
	bitsum = 1;
	for (i = letter; i > 'A'; i--)
	    bitsum *= 2;
    }
    else if ('a' <= letter && letter <= 'z')
    {
	bitsum = 67108864; /* 2^26 */
	for (i = letter; i > 'a'; i --)
	    bitsum *= 2;
    }

    return bitsum;
}

long fread_flag( FILE *fp)
{
    int number;
    char c;
    bool negative = false;

    do
    {
	c = getc(fp);
    }
    while ( isspace(c));

    if (c == '-')
    {
	negative = true;
	c = getc(fp);
    }

    number = 0;

    if (!isdigit(c))
    {
	while (('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'))
	{
	    number += flag_convert(c);
	    c = getc(fp);
	}
    }

    while (isdigit(c))
    {
	number = number * 10 + c - '0';
	c = getc(fp);
    }

    if (c == '|')
	number += fread_flag(fp);

    else if  ( c != ' ')
	ungetc(c,fp);

    if (negative)
	return -1 * number;

    return number;
}

FLAG *DIF( FLAG *var1, FLAG *var2, FLAG *buf, int size )
{
	int i;

	for( i = 0; i < (size); i++ )
	{
		buf[i] = (~( ~(var1[i]) | (var2[i]) ));
	}

	return buf;
}

FLAG *str_fread_flag( FILE *fp, const int size )
{
	int i;
	int read = -1;
static FLAG flag[MAX_FLAGS];

	STR_ZERO_STR( flag, MAX_FLAGS );

	for ( i = 0, read = fread_number(fp); read != -1; read = fread_number(fp), i++ )
	{
		if ( i < size )
			flag[i] = read;
	}

	return flag;
}

char *str_print_flags( FLAG *flags, const int size )
{
static char buf[1024];
	char buf2[1024];
	int i;
	
	for ( i = 0; i < size; i++ )
	{
		sprintf( buf2, "%d ", flags[i] );
		strcat( buf, buf2 );
	}

	sprintf( buf, "-1 " );
	strcat( buf, buf2 );

	return buf;
}

char *str_fwrite_flag( FLAG *flags, const int size, char *out )
{
	char buf[1024];
	int i;

	out[0] = '\0';

	for ( i = 0; i < size; i++ )
	{
		sprintf( buf, "%d ", flags[i] );
		strcat( out, buf );
	}

	sprintf( buf, "-1" );
	strcat( out, buf );

	return out;
}

bool STR_IS_ZERO( FLAG * var, int size )
{
	int i;

	for( i = 0; i < size; i++ )
	{
		if( var[i] != 0 )
			return true;
	}
	return false;
}

bool STR_AND( FLAG * var1, FLAG * var2, int size )
{
	int i;

	for( i = 0; i < size; i++ )
	{
		if( var1[i] & var2[i] )
			return true;
	}
	return false;
}

FLAG *aff_convert_fread_flag(FILE *fp)
{
static FLAG flags[MAX_FLAGS];
	long bits;
	long i;
	int j;

	STR_ZERO_STR( flags, MAX_FLAGS );

	bits = fread_flag( fp );
	if ( bits == 0 )
		return flags;

	for ( j = 1, i = 1; i <= ee && j < 32; j++, i*=2 )
	{
		if ( IS_SET( bits, i ) )
			STR_SET_BIT( flags, j );

		if ( i == ee )
			break;
	}

	return flags;
}

FLAG *aff2_convert_fread_flag(FILE *fp)
{
static FLAG flags[MAX_FLAGS];
	long bits;
	long i;
	int j;

	STR_ZERO_STR( flags, MAX_FLAGS );

	bits = fread_flag( fp );
	if ( bits == 0 )
		return flags;

	for ( j = 32, i = 1; i <= ee && j < MAX_FLAGS*FLAG_BITSIZE; j++, i*=2 )
	{
		if ( IS_SET( bits, i ) )
			STR_SET_BIT( flags, j );

		if ( i == ee )
			break;
	}

	return flags;
}

FLAG *act_convert_fread_flag(FILE *fp)
{
const int act_convert[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16,
							17, 18, 19, 20, 21, 0, 22, 0, 23, 24, 25, 26, 27 };
static FLAG flags[MAX_FLAGS];
	long bits;
	long i;
	int j;

	STR_ZERO_STR( flags, MAX_FLAGS );

	bits = fread_flag( fp );
	if ( bits == 0 )
		return flags;

	for ( j = 0, i = 1; i <= ee && j < 32; j++, i*=2 )
	{
		if ( IS_SET( bits, i ) )
			STR_SET_BIT( flags, act_convert[j] );

		if ( i == ee )
			break;
	}

	return flags;
}

FLAG *convert_fread_flag(FILE *fp)
{
static FLAG flags[MAX_FLAGS];

	STR_ZERO_STR( flags, MAX_FLAGS );
	STR_COPY_STR( flags, aff_convert_fread_flag( fp ), MAX_FLAGS );

	return flags;
}

FLAG *plr_convert_fread_flag(FILE *fp)
{
const int plr_convert[32] = { 1, 2, 3, 4, 5, 6, 7, 8, 0, 9, 10, 11, 12, 13, 0, 14, 15,
							  16, 0, 17, 18, 0, 19, 20, 21, 22, 23, 24, 25, 26, 27 };
static FLAG flags[MAX_FLAGS];
	long bits;
	long i;
	int j;

	STR_ZERO_STR( flags, MAX_FLAGS );

	bits = fread_flag( fp );
	if ( bits == 0 )
		return flags;

	for ( j = 0, i = 1; i <= ee && j < 32; j++, i*=2 )
	{
		if ( IS_SET( bits, i ) )
			STR_SET_BIT( flags, plr_convert[j] );

		if ( i == ee )
			break;
	}

	return flags;
}
