/*
	DooM/HereTic/HeXen/StriFe INFO.c processors, by Frans P. de Vries.

Derived from:

	DooM MaP StaTistics, by Frans P. de Vries.

	You are allowed to use any parts of this code in another program, as
	long as you give credits to the authors in the documentation and in
	the program itself.  Read the file README for more information.

	This program comes with absolutely no warranty.

	COMMON.C - Common main routines.
*/

#include <stdarg.h>
#include <ctype.h>
#include "common.h"
#include "wads.h"
#include "levels.h"
#include "print.h"
#include "info.h"


/*
	the global variables
*/
BCINT GameVersion = 0x00; /* which game and version? */
                          /* if you change this, bad things will happen to you... */
                 /* 0x00 = Shareware / Demo
                    0x01 = Registered
                    0x02 = Commercial (II/Final/64, Strife, Hexen)
                    0x04 = Ultimate / Shadow of the Serpent Riders
                    0x08 = Deathkings of the Dark Citadel / Veteran Edition / The Lost L
evels
                   +0x00 = Doom
                   +0x10 = Heretic
                   +0x20 = Strife
                   +0x40 = Hexen
                   +0x80 = Doom 64
                  */
Bool Verb         = FALSE; /* verbose logging? */
Bool XWik         = FALSE; /* wiki template? */
char **Wads       = NULL;  /* list of IWAD files */

int Weap = -1;     /* weapon number */
int Clss = -1;     /* class number */
int Thng = -1;     /* thing type number */
int Objt = -1;     /* object number */
char *Enum = NULL; /* enum string */
char *Stat = NULL; /* state string */
Bool Pain = FALSE; /* pain chance table */
Bool Mnst = FALSE; /* monsters pc/hp */
Bool AllT = FALSE; /* all thing types by number */
Bool AllC = FALSE; /* all thing types by class */
Bool AllO = FALSE; /* all mobjs */

TMPL_varlist *tpldata = NULL; /* template data */

char fieldlist[LISTSIZE], field2list[LISTSIZE], fieldentry[ENTRYSIZE];


OptDesc options[] =  /* description of the command line options */
{
/* short/long names   type           msg if true/changed  msg if false  store value */
	{ "V",  "VERBOSE",  OPT_BOOLEAN,   "Verbose log",               NULL, &Verb },
	{ "X",  "XWIKI",    OPT_BOOLEAN,   "Use wiki template",         NULL, &XWik },
	{ "",   "FILE",     OPT_STRINGLIST,"IWAD files",                NULL, &Wads },
	{ "W",  "WEAPON",   OPT_INTEGER,   "Weapon number",             NULL, &Weap },
	{ "S",  "CLASS",    OPT_INTEGER,   "Class number",              NULL, &Clss },
	{ "T",  "THING",    OPT_INTEGER,   "Thing type number",         NULL, &Thng },
	{ "O",  "OBJECT",   OPT_INTEGER,   "Object number",             NULL, &Objt },
	{ "E",  "ENUM",     OPT_STRING,    "Enum string",               NULL, &Enum },
	{ "Z",  "STATE",    OPT_STRING,    "State string",              NULL, &Stat },
	{ "P",  "PAINTAB",  OPT_BOOLEAN,   "Pain chances",              NULL, &Pain },
	{ "M",  "MONSTERS", OPT_BOOLEAN,   "Monsters PC/HP",            NULL, &Mnst },
	{ "N",  "ALLTYPE",  OPT_BOOLEAN,   "All thing types by number", NULL, &AllT },
	{ "C",  "ALLCLASS", OPT_BOOLEAN,   "All thing types by class",  NULL, &AllC },
	{ "A",  "ALLMOBJ",  OPT_BOOLEAN,   "All mobjs",                 NULL, &AllO },
	{ NULL, NULL,       OPT_END,       NULL,                        NULL, NULL  }
};

/*
	the local function prototypes
*/
void ParseCommandLineOptions( int, char *[], char *);
void Usage( char *);
void Credits( char *, char *);

/*
	the common part of the main program
*/
void Common( int argc, char *argv[], char *program, char *title)
{
	Credits( program, title);
	argv++;
	argc--;
	/* read command line options */
	ParseCommandLineOptions( argc, argv, program);

	if (Pain)
	{
		PainChances();
		exit(0);
	}
	if (Mnst)
	{
		MonstersPcHp();
		exit(0);
	}

	InitTypes(); // for lookups
	if (AllT)
	{
		if (!Wads)
			ProgError( "IWAD files are required");
		while (Wads[0])
		{
			OpenIWad( Wads[0]);
			Wads++;
		}
		ThingTypes();
		CloseIWads();
		exit(0);
	}
	if (AllC)
	{
		if (!Wads)
			ProgError( "IWAD files are required");
		while (Wads[0])
		{
			OpenIWad( Wads[0]);
			Wads++;
		}
		ClassTypes();
		CloseIWads();
		exit(0);
	}

	if (AllO)
	{
		printf( "\n==================================================\n");
		for (Objt = 0; Objt < NUMMOBJTYPES; Objt++)
		{
			PrintData();
			printf( "==================================================\n");
		}
		exit(0);
	}

	if (Stat != NULL)
	{
		PrintState();
		exit(0);
	}

	if (Weap != -1
#ifdef HXINFO
	    && Clss != -1
#endif
	   )
	{
		if (XWik)
		{
			if (!Wads)
				ProgError( "IWAD files are required");
			while (Wads[0])
			{
				OpenIWad( Wads[0]);
				Wads++;
			}
		}

		PrintWeapon();

		if (XWik)
			CloseIWads();
	}
	else
	{
		if (Thng <= -1 && Objt <= -1 && Enum == NULL)
			ProgError( "no Thing or Object number or Enum string specified");
		if ((Thng != -1 && Objt != -1) ||
		    (Thng != -1 && Enum != NULL) ||
		    (Objt != -1 && Enum != NULL))
			ProgError( "must specify one of Thing number, Object number or Enum string");

		if (XWik)
		{
			if (!Wads)
				ProgError( "IWAD files are required");
			while (Wads[0])
			{
				OpenIWad( Wads[0]);
				Wads++;
			}
		}

		PrintData();

		if (XWik)
			CloseIWads();
	}

	if (XWik) {
		TMPL_write( "../wikidata.tpl", NULL, NULL, tpldata, stdout, stderr);
		TMPL_free_varlist( tpldata);
		tpldata = NULL;
	}
	exit( 0);
}


/*
	append a string to a null-terminated string list
*/
void AppendItemToList( char ***list, char *item)
{
	int i = 0;

	if (*list)
	{
		/* count the number of elements in the list (last = null) */
		while ((*list)[i])
			i++;
		/* expand the list */
		*list = (char **) ResizeMemory( *list, (i + 2) * sizeof(char **));
	}
	else
		/* create a new list */
		*list = (char **) GetMemory( 2 * sizeof(char **));

	/* append the new element */
	(*list)[i] = item;
	(*list)[i + 1] = NULL;
}

/*
	handle command line options
*/
void ParseCommandLineOptions( int argc, char *argv[], char *program)
{
	int optnum;

	while (argc > 0)
	{
		if (argv[0][0] != '-' && argv[0][0] != '+')
			ProgError( "options must start with '-' or '+'");
		strupr( argv[0]);
		if (!strcmp( argv[0], "-?") || !strcmp( argv[0], "-H") ||
		    !strcmp( argv[0], "-HELP"))
		{
			Usage( program);
			exit( 0);
		}
		for (optnum = 0; options[optnum].opt_type != OPT_END; optnum++)
		{
			if (!strcmp( &(argv[0][1]), options[optnum].short_name) ||
			    !strcmp( &(argv[0][1]), options[optnum].long_name))
			{
				switch (options[optnum].opt_type)
				{
				case OPT_BOOLEAN:
					if (argv[0][0] == '+') /* '+/-' reversed for print flags */
					{
						*((Bool *) (options[optnum].data_ptr)) = TRUE;
						if (options[optnum].msg_if_true)
							printf( "%s.\n", options[optnum].msg_if_true);
					}
					else
					{
						*((Bool *) (options[optnum].data_ptr)) = FALSE;
						if (options[optnum].msg_if_false)
							printf( "%s.\n", options[optnum].msg_if_false);
					}
					break;
				case OPT_INTEGER:
					if (argc <= 1)
						ProgError( "missing argument after \"%s\"", argv[0]);
					argv++;
					argc--;
					*((int *) (options[optnum].data_ptr)) = atoi( argv[0]);
					if (options[optnum].msg_if_true)
						printf( "%s: %d.\n", options[optnum].msg_if_true, atoi( argv[0]));
					break;
				case OPT_STRING:
					if (argc <= 1)
						ProgError( "missing argument after \"%s\"", argv[0]);
					argv++;
					argc--;
					*((char **) (options[optnum].data_ptr)) = argv[0];
					if (options[optnum].msg_if_true)
						printf( "%s: %s.\n", options[optnum].msg_if_true, argv[0]);
					break;
				case OPT_STRINGACC:
					if (argc <= 1)
						ProgError( "missing argument after \"%s\"", argv[0]);
					argv++;
					argc--;
					AppendItemToList( (char ***) options[optnum].data_ptr, argv[0]);
					if (options[optnum].msg_if_true)
						printf( "%s: %s.\n", options[optnum].msg_if_true, argv[0]);
					break;
				case OPT_STRINGLIST:
					if (argc <= 1)
						ProgError( "missing argument after \"%s\"", argv[0]);
					while (argc > 1 && argv[1][0] != '-' && argv[1][0] != '+')
					{
						argv++;
						argc--;
						AppendItemToList( (char ***) options[optnum].data_ptr, argv[0]);
						if (options[optnum].msg_if_true)
							printf( "%s: %s.\n", options[optnum].msg_if_true, argv[0]);
					}
					break;
				default:
					ProgError( "unknown option type (BUG!)");
				}
				break;
			}
		}
		if (options[optnum].opt_type == OPT_END)
			ProgError( "invalid argument: \"%s\"", argv[0]);
		argv++;
		argc--;
	}
}


/*
	output the program usage
*/
void Usage( char *program)
{
	printf( "Usage:\n");
	printf( "%s [+v] [+a|p|m] [+n|c -file <wad_files>]\n", program);
	printf( "       [-w <num>] [-t <thing>] [-o <obj>] [-e <enum>] [-z <state>]\n");
	printf( "       [+x -file <wad_files>]\n");
	printf( "   +v    Display verbose logging to stderr (also +verbose)\n");
	printf( "   +x    Use wiki template for thing data (also +xwiki)\n");
	printf( "   +a    Display all mobjs (also +allmobj)\n");
	printf( "   +p    Display pain chance table (also +paintab)\n");
	printf( "   +m    Display monsters with pain chance & hit points (also +monsters)\n");
	printf( "   +n    Display all thing types by number (also +alltype)\n");
	printf( "   +c    Display all thing types by class (also +allclass)\n");
	printf( "   -file Add a list of IWAD files to be loaded (order: limited, full)\n");
	printf( "   -w    Display data for specified weapon (also -weapon)\n");
#ifdef HXINFO
	printf( "   -s    Required class for -w (also -class); 0=fighter, 1=cleric, 2=mage\n");
#endif
	printf( "   -t    Display data for specified thing type (also -thing)\n");
	printf( "   -o    Display data for specified object (also -object)\n");
	printf( "   -e    Display data for specified enum (also -enum)\n");
	printf( "   -z    Display data for specified state (also -state)\n");
	printf( "Put a '+' instead of a '-' before boolean options to reverse their effect\n");
}

/*
	output the credits of the program
*/
void Credits( char *program, char *title)
{
	char *prog = strdup( program);

	strupr( prog);
	printf( "%s: %s INFO.c processor, ver %s\n", prog, title, INFO_VERSION);
	printf( " By Frans P. de Vries <fpv@gamers.org>\n");
	FreeMemory( prog);
}


/*
	terminate the program reporting an error
*/
void ProgError( char *errstr, ...)
{
	va_list args;

	va_start( args, errstr);
	printf( "\nProgram Error: *** ");
	vprintf( errstr, args);
	printf( " ***\n");
	va_end( args);
	/* clean up & free space */
	ForgetLevelData();
	CloseIWads();
	exit( 1);
}

/*
	convert string to uppercase
*/
char *strupr( char *str)
{
	char *s = str;

	if (str != NULL)
		while (*s)
		{
			if (islower( *s))
				*s = toupper( *s);
			s++;
		}

	return s;
}

/* vim:set noexpandtab tabstop=2 softtabstop=2 shiftwidth=2: */
