/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    $Id: init.c 636 2007-07-20 05:07:57Z disconn3ct $
*/

#include "defs.h"

#define DEFAULT_FPS 20
#define DEBUG_FILE	"debug.txt"
#define LOG_FILE	"log.txt"
#ifdef _WIN32
#define BINARY_NAME "qwdtools.exe"
#else
#define BINARY_NAME "qwdtools"
#endif

extern int com_argc;
extern char *com_argv[MAX_NUM_ARGVS];


void Help (void)
{
	Sys_Printf("usage: " BINARY_NAME " [-options] demoname1 demoname2 ...\n"
	           "example:\n"
	           BINARY_NAME " -c -o * -od out mydemo.qwd\nwill convert demo 'mydemo.qwd' to 'out/mydemo.mvd'\n\n");

	Sys_Printf("available options: (argument in [] is optional)\n");
	Sys_Printf("-bd path               changes current directory\n");
	Sys_Printf("-qd path               path to qizmo dir, e/g: -qd ../qizmo\n");
	Sys_Printf("-od path               output directory path\n");
	Sys_Printf("-o name                output file name (default: *)\n");
	Sys_Printf("-debug_file name       debug file name (default: debug.txt)\n");
	Sys_Printf("-log_file name         log file name (default: log.txt)\n");
	Sys_Printf("-fps num               demo fps (default: 20)\n");
	Sys_Printf("-fs                    filters spectator chats\n");
	Sys_Printf("-fq                    filters qizmo observers chats\n");
	Sys_Printf("-fc                    filters all chats\n");
	Sys_Printf("-ft                    filters team chats\n");
	Sys_Printf("-msg                   msg level (same as QW's msg command)\n");
	Sys_Printf("-c                     converts to mvd (assumed if no options are given)\n");
	Sys_Printf("-m                     marges multiple demos to one mvd demo\n");
	//Sys_Printf("-analyse/-a            - analysing demo\n");
	Sys_Printf("-log                   creates log file\n");
	Sys_Printf("-debug                 prints way too much messages\n");
#ifdef _WIN32
	Sys_Printf("-wait                  \"Press any key to continue\"\n");
#endif
	Sys_Printf("-noini                 ini file won't be executed\n");
	Sys_Printf("-ini path              name of ini file (default: qwdtools.ini)\n");
	Sys_Printf("-stdin [format]        file from stdin  (default format: qwd)\n");
	Sys_Printf("-stdout                result to stdout (first found option: -c/-log/-debug)\n");
	//Sys_Printf("\n" VERSION " (c) 2001 Bartlomiej Rychtarski (highlander@gracz.net)\nhttp://qwex.n3.net/\n");
}

#ifdef _WIN32
BOOL WINAPI CtrlC( DWORD dwCtrlType )
{
	switch (dwCtrlType)
	{
			case CTRL_C_EVENT:
			if (sworld.options |= O_SHUTDOWN)
				exit(3);

			Sys_Printf("Shutting down...\n");
			if (sworld.options & O_WAITFORKBHIT)
				SetConsoleTitle("qwdtools  ctrl-c issued");

			sworld.options |= O_SHUTDOWN;
			return true;
			case CTRL_BREAK_EVENT:
			if (sworld.options |= O_SHUTDOWN)
				exit(3);

			Sys_Printf("Shutting down...\n");
			if (sworld.options & O_WAITFORKBHIT)
				SetConsoleTitle("qwdtools  ctrl-break issued");

			sworld.options |= O_SHUTDOWN;
			return true;
	}

	return false;
}

void CtrlH_Init (void)
{
	SetConsoleCtrlHandler(&CtrlC, true);
}
#else
void CtrlC( int sig )
{
	if (sworld.options & O_SHUTDOWN)
		exit(3);

	Sys_Printf("Shutting down...\n");

	sworld.options |= O_SHUTDOWN;

	signal(sig, CtrlC);
}

void CtrlH_Init (void)
{
	signal(SIGINT, CtrlC);
	signal(SIGTERM, CtrlC);
}
#endif
/*
===================
World_Init
 
Initial values
===================
*/
void CleanName_Init ();
void World_Init(void)
{
	int i;

	memset(sourceName, 0, sizeof(sourceName));

	strlcpy(qizmoDir, "", sizeof(qizmoDir));
	strlcpy(outputDir, "", sizeof(outputDir));

	sworld.fps = DEFAULT_FPS;
	sworld.range = 5;
	strlcpy (sworld.debug.name, DEBUG_FILE, MAX_OSPATH);
	strlcpy (sworld.log.name, LOG_FILE, MAX_OSPATH);
	strlcpy (sworld.demo.name, "*", MAX_OSPATH);
	strlcpy (sworld.analyse.name, "*.txt", MAX_OSPATH);

	net_message.maxsize = sizeof(net_message_buffer);
	net_message.data = net_message_buffer;

	if ((i = CheckParm("-bd")) == 0)
		i = CheckParm("-base_dir");

	if (i && i + 1 < com_argc)
	{
#ifdef _WIN32
		SetCurrentDirectory(com_argv[i+1]);
#else
		chdir(com_argv[i+1]);
#endif

	}

#ifdef _WIN32
	GetCurrentDirectory(sizeof(currentDir), currentDir);
#else
	getcwd(currentDir, sizeof(currentDir));
#endif

	//Sys_Printf("arg:%s\ncur:%s\n", com_argv[0], currentDir);

#ifdef _WIN32
	ConsoleInHndl = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0,NULL);
	ConsoleOutHndl = CreateFile("CONOUT$", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,0,NULL);
	if (CheckParm("-wait") || CheckParm("-waitforkbhit"))
		sworld.options |= O_WAITFORKBHIT;
#endif

	if (CheckParm("-stdout"))
		sworld.options |= O_STDOUT;

	CleanName_Init ();
}

/*
===================
ParseArgv

Parse cmdline options
===================
*/

char stdintype[32] = "";
char dummy[64];
int  whatToStdout = 0;

#ifdef _MSC_VER
#pragma warning( disable : 4133 4047)
#endif
param_t params[] =
	{
		{"-qizmo_dir",			"-qd",	TYPE_S, {(char *) qizmoDir}, sizeof(qizmoDir)},
		{"-output_dir",			"-od",	TYPE_S, {(char *) outputDir}, sizeof(outputDir)},
		{"-output",				"-o",	TYPE_S, {(char *) sworld.demo.name}, sizeof(sworld.demo.name)},
		{"-debug_file",			NULL,	TYPE_S, {(char *) sworld.debug.name}, sizeof(sworld.debug.name)},
		{"-log_file",			NULL,	TYPE_S, {(char *) sworld.log.name}, sizeof(sworld.log.name)},
		{"-fps",				NULL,	TYPE_I, {(char *) &sworld.fps}},
		{"-filter_spectalk",	"-fs",	TYPE_O, {(char *) O_FS}},
		{"-filter_qizmotalk",	"-fq",	TYPE_O, {(char *) O_FQ}},
		{"-filter_chats",		"-fc",	TYPE_O, {(char *) O_FC}},
		{"-filter_teamchats",	"-ft",	TYPE_O, {(char *) O_FT}},
		{"-convert",			"-c",	TYPE_O, {(char *) O_CONVERT}},
		{"-analyse",			"-a",	TYPE_O, {(char *) O_ANALYSE}},
		{"-log",				NULL,	TYPE_O, {(char *) O_LOG}},
		{"-debug",				NULL,	TYPE_O, {(char *) O_DEBUG}},
#ifdef _WIN32
		{"-waitforkbhit",		"-wait",TYPE_O, {(char *) O_WAITFORKBHIT}},
#endif
		{"-stdin",				NULL,	(type_t) (TYPE_O | TYPE_S), {(char *) stdintype}, sizeof(stdintype), (int) O_STDIN},
		{"-stdout",				NULL,	TYPE_O, {(char *) O_STDOUT}},
		{"-noini"},
		{"-base_dir",			"-bd",	TYPE_S, {(char *) currentDir}, sizeof(currentDir)},
		{"-msglevel",			"-msg",	TYPE_I, {(char *) &sworld.msglevel}},
		{"-marge",				"-m",	TYPE_O, {(char *) O_MARGE}},
		{"-range",				"-r",	TYPE_I, {(char *) &sworld.range}},
		{NULL}
	};

#ifdef _MSC_VER
#pragma warning( default : 4133 4047)
#endif

param_t *FindParam(char *parm)
{
	param_t *param;

	for (param = params; param->name; param++)
		if (!strcasecmp(parm, param->name) || (param->shname && !strcasecmp(parm, param->shname)))
			return param;

	return NULL;
}

void ParseArgv(void)
{
	int	i, o;
	param_t	*param;
	char	qwz_files[1024] = "", *ext, *arg, tmp[MAX_OSPATH];

	if (CheckParm("-help"))
	{
		Help();
		Sys_Exit(1);
	}

	// get qizmo dir before args are parsed
	if ((i = CheckParm("-qd")) == 0)
		i = CheckParm("-qizmo_dir");

	if (i && i + 1 < com_argc)
		strlcpy(qizmoDir, com_argv[i+1], sizeof(qizmoDir));

	// parse args
	for (i = 1; i < com_argc; i++)
	{
		arg = com_argv[i];
		// if starts from -- ignore or scream that unknown
		if (arg[0] == '-' && arg[1] == '-')
			arg++;

		if ( (param = FindParam(arg)) == NULL)
		{
			// if starts from '-' it's an unrecognized option
			if (arg[0] == '-')
			{
				Sys_Printf("unrecognized option: %s\n", arg);
				Help();
				Sys_Exit(1);
			}

			// it's a file name, first add default extension if needed
			// then add to file list
			strlcpy(tmp, arg, sizeof(tmp));
			COM_DefaultExtension(tmp, ".qwd");
			sworld.sources += AddToFileList(sworld.filelist, tmp);

			// check for qwz extension
			ext = COM_FileExtension (arg);
			if (!strcasecmp(ext, "qwz"))
			{
				if (!strcmp(qizmoDir, ".") || !strcmp(qizmoDir, "./")
#ifdef _WIN32
				        || !strcmp(qizmoDir, ".\\")
#endif
				   )
					qizmoDir[0] = 0;

				if (strstr(arg, ":") || !qizmoDir[0])
					strlcat(qwz_files, va("\"%s\" ", arg), sizeof(qwz_files));
				else
					strlcat(qwz_files, va("\"%s/%s\" ", currentDir, arg), sizeof(qwz_files));
			}

			continue;
		}

		// if started from -- ignore
		if (arg != com_argv[i])
			continue;

		// run option
		if (param->type & TYPE_S)
		{
			i++;
			if (i < com_argc && com_argv[i][0] != '-')
			{
				strlcpy(param->opt1.str, com_argv[i], param->str_len);
			}
			else
			{
				// if it's stdin assume qwd
				if (param->opt2 == O_STDIN)
				{
					strlcpy(param->opt1.str, "qwd", param->str_len);
					i--;
				}
				else
				{
					Sys_Printf("init: missing argument for %s\n", arg);
					Help();
					Sys_Exit(1);
				}
			}

			if ((param->opt1.str = currentDir))
			{
#ifdef _WIN32
				SetCurrentDirectory(currentDir);
				GetCurrentDirectory(sizeof(currentDir), currentDir);
#else
				chdir(currentDir);
				getcwd(currentDir, sizeof(currentDir));
#endif

			}
		}

		if (param->type & TYPE_I)
		{
			i++;
			if (i < com_argc && com_argv[i][0] != '-')
			{
				*param->opt1.Int = Q_atoi(com_argv[i]);
			}
			else
			{
				Sys_Printf("init: missing argument for %s\n", arg);
				Help();
				Sys_Exit(1);
			}
		}

		if (param->type & TYPE_O)
		{
			if (param->opt2)
				o = param->opt2;
			else
				o = param->opt1.Opt;
			if (!whatToStdout && (o & JOB_TODO))
				whatToStdout = o;
			sworld.options |= o;
		}
	}

	if (!(sworld.options & JOB_TODO))
	{
		sworld.options |= O_CONVERT;
		whatToStdout = O_CONVERT;
	}

	if (sworld.options & O_MARGE)
		sworld.options -= (sworld.options & (O_CONVERT | O_STDIN));

	// change translation mode for stdin
	if (sworld.options & O_STDIN)
	{
		sworld.sources = 1;
		sworld.from->file = stdin;

		// free file list, files from argument list won't be used
		FreeFileList(sworld.filelist);
		AddToFileList(sworld.filelist, "");
#ifdef _WIN32
		if (_setmode (_fileno(sworld.from->file), _O_BINARY) == -1)
			Sys_Error("failed to set binary mode for stdin");
#endif

	}
	else
	{
		if (qwz_files[0])
		{
			// decompress qwz files
			if (!OpenQWZ(qwz_files))
				Sys_Printf("Couldn't decompress qwz file(s)\n");
		}
	}

	if (sworld.filelist[0].list == NULL/* && !(sworld.options & O_STDIN)*/)
	{
		Sys_Printf("demo name not specified\n");
		Help();
		Sys_Exit(1);
	}

	// fps
	if (sworld.fps < 4) // 1000/4 -> 250ms
		sworld.fps = 4;
	if (sworld.fps > 1000)
		sworld.fps = 1000;

	if (sworld.range < 1) sworld.range = 1;
	if (sworld.range > 15) sworld.range = 15;
}


FILE *openFile (char *filename, char *name, int opt, int mode)
{
	FILE *file;

	if (sworld.options & O_STDOUT && whatToStdout == opt)
	{
		file = stdout;
		filename = "stdout";
#ifdef _WIN32
		if (_setmode (_fileno(stdout), mode) == -1)
		{
			Sys_Printf("failed to set %s mode for stdout", mode == _O_TEXT ? "text" : "binary");
			return NULL;
		}
#endif

	}
	else
#ifdef _WIN32
		file = fopen (filename, mode == _O_TEXT ? "wt" : "wb");
#else
		file = fopen (filename, "w");
#endif

	if (file == NULL)
		Sys_Printf ("couldn't open for writing: %s.\n", filename);
	else
		Sys_Printf("%s: %s\n", name, filename);

	return file;
}

/*
===================
Files_Init
 
Init file system
===================
*/

int Files_Init (int options)
{
	char *p, log1[MAX_OSPATH], debug[MAX_OSPATH], convert[MAX_OSPATH], out[MAX_OSPATH], analyse[MAX_OSPATH], d[MAX_OSPATH];
	int job = 0, i;

	if (!strcmp(currentDir, sworld.from->path))
		strlcpy(d, ".", sizeof(d));
	else
		strlcpy(d, sworld.from->path, sizeof(d));

	strlcpy(out, TemplateName(d, outputDir, "@"), sizeof(out));
	//Sys_Printf("%s, %s, %s, %s\n", sworld.from->path, d, outputDir, out);

	if (options & O_MARGE)
		p = "marge";
	else
		p = sworld.from->name;


	strlcpy(convert, TemplateName(p, sworld.demo.name, "*"), sizeof(convert));
	ForceExtension(convert, ".mvd");

	strlcpy(log1, TemplateName(p, sworld.log.name, "*"), sizeof(log1));
	strlcpy(debug, TemplateName(p, sworld.debug.name, "*"), sizeof(debug));
	strlcpy(out, TemplateName(p, out, "*"), sizeof(out));
	strlcpy(analyse, TemplateName(p, sworld.analyse.name, "*"), sizeof(analyse));

	if (out[0])
	{
		Sys_mkdir(out);
		p = out + strlen(out) - 1;
		if (*p != '/' && *p != '\\')
		{
			*++p = '/';
			*++p = 0;
		}
	}

	snprintf(analyse, sizeof(analyse), "%s", va("%s%s", out, analyse));
	snprintf(convert, sizeof(convert), "%s", va("%s%s", out, convert));
	snprintf(log1, sizeof(log1), "%s", va("%s%s", out, log1));
	snprintf(debug, sizeof(debug), "%s", va("%s%s", out, debug));

	// open source file(s)
	for (i = 0, from = sources; i < sworld.fromcount; i++, from++)
	{
		if (strcmp(currentDir, sworld.from[i].path))
			strlcpy(sworld.from[i].name, va("%s%s", sworld.from[i].path, sworld.from[i].name), MAX_OSPATH);

		if (!strcmp(convert, sworld.from[i].name) && options & O_CONVERT)
		{
			Sys_Printf("Can't convert from %s to %s!\n", sworld.from[i].name, convert);
			options -= O_CONVERT;
		}

		if (sworld.options & O_STDIN)
		{
			if (from->format == qwd)
				strlcat(sworld.from[i].name, " (qwd)", sizeof(sworld.from[i].name));
			else
				strlcat(sworld.from[i].name, " (mvd)", sizeof(sworld.from[i].name));
		}
		else if ((sworld.from[i].filesize = FileOpenRead(sworld.from[i].name, &sworld.from[i].file)) == -1)
		{
			Sys_Printf("couldn't open for reading: %s\n", sworld.from[i].name);
			return 0;
		}

		world.demossize += sworld.from[i].filesize;
	}

	if (options & O_MARGE)
	{
		Sys_Printf("sources: %s\n", sworld.from[0].name);
		for (i = 1; i < sworld.fromcount; i++)
			Sys_Printf("         %s\n", sworld.from[i].name);
	}
	else
		Sys_Printf("source: %s\n", sworld.from[0].name);

	// open output files
	if (options & O_CONVERT)
	{
		sworld.demo.file = openFile(convert, " converting to", O_CONVERT, _O_BINARY);
		if (sworld.demo.file)
			job |= O_CONVERT;
	}

	if (options & O_MARGE)
	{
		sworld.demo.file = openFile(convert, " marging to", O_CONVERT, _O_BINARY);
		if (sworld.demo.file)
			job |= O_MARGE;
	}

	if (options & O_DEBUG)
	{
		sworld.debug.file = openFile(debug, " debug", O_DEBUG, _O_TEXT);
		if (sworld.debug.file)
			job |= O_DEBUG;
	}

	if (options & O_LOG)
	{
		sworld.log.file = openFile(log1, " log", O_LOG, _O_TEXT);
		if (sworld.log.file)
			job |= O_LOG;
	}

	if (options & O_ANALYSE)
	{
		sworld.analyse.file = openFile(analyse, " analyse", O_ANALYSE, _O_TEXT);
		if (sworld.analyse.file)
			job |= O_ANALYSE;
	}

	return job;
}

void Load_ini (void)
{
	char *buf;
	char name[MAX_OSPATH];
	char path[MAX_OSPATH];
	int i;

	if (CheckParm("-noini"))
		return;

	//get path to program
	strlcpy(path, getPath(com_argv[0]), sizeof(path));
	// if path == current dir, use short name
	if (!strcasecmp(path, va("%s/", currentDir)))
		strlcpy(name, com_argv[0] + strlen(path), sizeof(name));
	else
		strlcpy(name, com_argv[0], sizeof(name));

	COM_StripExtension(name, name);
	ForceExtension(name, ".ini");

	//Sys_Printf("name:%s\nargv:%s\n", name, com_argv[0]);

	if ( (i = CheckParm("-ini")) != 0)
	{
		if (i+1 < com_argc)
		{
			strlcpy(name,com_argv[i+1], sizeof(name));
			RemoveParm(i);
		}
		else
		{
			Help();
			Sys_Exit(1);
		}

		RemoveParm(i);
	}

	buf = (char*) LoadFile(name);
	if (!buf)
	{
		Sys_Printf("couldn't load %s\n", name);
		return;
	}

	ReadIni(buf);

	Q_free(buf);
}
