/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "viewer_base/viewer_base.h"
		#include <iostream>
		#include "io_archive/io_archive.h"
		#include "scene3d/scene.h"
		#include "core/engine.h"
		#include "filesystem/filesystem.h"
		#include "filesystem/io_cfile.h"
		#include "platform.h"

		using namespace GS;


// [EJ] 4/4: Do not use Log to output the command line as it will be eaten by a release build.

//------------------------------------------------------------------------------
void				ViewerBase::PrintHeader()
{
	std::cout << "GameStart stand-alone.\n";
	std::cout << "http://www.gamestart3d.com\n";
	std::cout << "Version: " << Core::Version << "\n";
	std::cout << "Emmanuel Julien 2001-2013.\n\n";
}
String				ViewerBase::GetBaseCommandLineParm() const
{
	String parm;

	parm << "Basic usage:\n\n";
	parm << "-P                 : Execute a project (default: execute scene).\n";
	parm << "-C <config path>   : Configuration file path.\n";
	parm << "\n";

	parm << "Data source (default: -f ./):\n\n";
	parm << "-f <base path>     : Input data from the file system.\n";
	parm << "-A <archive path>  : Input data from an archive.\n";
	parm << "-version <name>    : Run a specific project version.\n";
	parm << "\n";

	parm << "Resource path:\n\n";
	parm << "-CC <path>         : Mount a file system directory as core.\n";
	parm << "-MD <path> <mount> : Mount a file system directory.\n";
	parm << "-S <path>          : Add search path.\n";
	parm << "\n";

	parm << "Data output (default: -r gl2 -m al):\n\n";
	parm << "-r <ID>            : Output renderer ('list' for details).\n";
	parm << "-m <ID>            : Output audio mixer ('nengine dummy -m list' for valid IDs).\n";
	parm << "-w <Width>         : Display width in pixels.\n";
	parm << "-h <Height>        : Display height in pixels.\n";
	parm << "\n";

	parm << "Script interface:\n\n";
	parm << "-I <include>       : Include a script.\n";
	parm << "-Ds <var> <string> : Define and initialize a variable.\n";
	parm << "-Di <var> <int>    : Define and initialize a variable.\n";
	parm << "-Df <var> <float>  : Define and initialize a variable.\n";
	parm << "\n";

	parm << "Raytracer interface (scene view only):\n\n";
	parm << "-R <path>          : Raytrace each frame to a directory.\n";
	parm << "-Rw <int>          : Specify the raytracer frame width.\n";
	parm << "-Rh <int>          : Specify the raytracer frame height.\n";
	parm << "-Raa <int>         : Specify the raytracer AA grid size (default: 4).\n";
	parm << "\n";

	parm << "Program flags:\n\n";
	parm << "-remote            : Start the viewer in remote mode (must be first argument).\n";
	parm << "-remote_port <int> : Set the remote mode listening port.\n";
	parm << "\n";
	parm << "-ignore_esc        : Do not exit when escape is pressed.\n";
	parm << "-ignore_missing    : Do not exit on missing resource.\n";
	parm << "-ignore_bootstrap  : Ignore the bootstrap file.\n";
	parm << "\n";
	parm << "-tool_mode	        : Execute as if ran in the editor.\n";
	parm << "-safe_mode         : Enable renderer/mixer safe-mode.\n";
	parm << "\n";
	parm << "-enable_pause      : Enable 'p' key to pause engine.\n";
	parm << "\n";
	parm << "-fallback_disk_fs  : Enable disk file system fallback.\n";
	parm << "\n";
	parm << "-enable_profiler   : Enable on-screen performance profiler.\n";
	parm << "-memory_profiler   : Enable on-screen memory profiler.\n";
	parm << "\n";
	parm << "-log_level <mask>  : Set the engine log level mask (eg. -log_level !S*).\n";
	parm << "                     n: None\n";
	parm << "                     s: Standard\n";
	parm << "                     H: Header\n";
	parm << "                     *: Warning\n";
	parm << "                     !: Error\n";
	parm << "                     V: Verbose\n";
	parm << "                     S: Script\n";
	parm << "                     a: All (default)\n";
	parm << "-time_live <sec>   : Set runtime time-to-live in seconds.\n";

	return parm;
}
void				ViewerBase::PrintUsage()
{
	std::cout << "Usage: gsviewer input(.nms|.ngp) <-P> <-f|-A> <-S>\n\n";
	std::cout << GetBaseCommandLineParm() << "\n";
	PrintAdditionalUsage();
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool				ViewerBase::LocateAndParseBootstrap(Stack <String> &_arg)
{
	SharedPtr <IO::Base> io;
	AutoPtr <IO::Handle> h;

	// Look for a naked bootstrap file.
	if	((h = Platform::Get().io->Open("bootstrap.txt")) != NULL)
		session_source = SessionSourceFilesystem;

	else
	{
		// Mount core to default archive.
		io = new IO::Archive("@root/native/000.gsa");
		Platform::Get().io->Mount(io, "@core/");

		// Look for an archived bootstrap.
		h = Platform::Get().io->Open("@core/bootstrap.txt");
		if	(h.IsNull())
		{
			Platform::Get().io->Unmount("@core/");	// [EJ] unmount on fail to locate bootstrap
			return false;
		}

		// Set archive as the data source.
		session_source = SessionSourceArchiveBootstrap;
		Platform::Get().io->Mount(io);
	}

	// Parse bootstrap.
	String bootstrap;

	size_t s = h->GetSize();
	Array <char> s_buffer(s);
	h->Read(s_buffer, s);

	bootstrap.Set(s_buffer.c_ptr(), &s_buffer.c_ptr()[s]);
	bootstrap.Split(" ", _arg, '\"');

	h = NULL;
	return true;
}
void				ViewerBase::SetupVersion(const char *name)
{
	using namespace NML;

	File file;
	if	(!Parser::Load("@root/.reserved/versions.rls", file))
		return;

	if	(Tag *versions = file.GetTag("Versions;"))
	{
		NMLTagForeach(v, *versions)
			if	(Tag *n = v->GetTypedTag("Name", Variant::VariantString))
				if	(String(n->GetString()) == name)
					if	(Tag *s = v->GetTypedTag("BootstrapScript", Variant::VariantString))
					{
						bootstrap_script = s->GetString();
						return;
					}
	}
}
//------------------------------------------------------------------------------

//-----------------------------------------------------------------------------
bool				ViewerBase::ParseCommandLine(Stack <String> &_arg)
{
	if	((_arg.GetCount() < 1) && !LocateAndParseBootstrap(_arg))
		__ERR__(PrintUsage(), false)

	session_type = SessionScene;	// assume scene session

	// Remote is a special flag
	int n = 0;
	if	(_arg[0] != "-remote")
		input_path = _arg[n++];

	// Parse optional arguments.
	int narg = (int)_arg.GetCount();
	for ( ; n < narg; ++n)
	{
		String arg(_arg[n]);

				if	(arg == "-P")
					session_type = SessionProject;

		// Version.
		else	if	(arg == "-version")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-version: Missing version name.\n", false);
			SetupVersion(_arg[n]);
		}

		// Time to live.
		else	if	(arg == "-time_live")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-time_live: Missing mask.\n", false);
			time_live = String::atoi(_arg[n]);
		}

		// Log filter.
		else	if	(arg == "-log_level")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-log_level: Missing mask.\n", false);

			uint mask = 0;
			for (const char *p_flag = _arg[n]; p_flag[0]; ++p_flag)
				switch (p_flag[0])
				{
					case 'n':	mask = EngineLogNone;		break;
					case 's':	mask |= EngineLogStandard;	break;
					case 'H':	mask |= EngineLogHeader;	break;
					case '*':	mask |= EngineLogWarning;	break;
					case '!':	mask |= EngineLogError;		break;
					case 'V':	mask |= EngineLogVerbose;	break;
					case 'S':	mask |= EngineLogScript;	break;
					case 'a':	mask |= EngineLogAll;		break;
				}

			LogSystem::Get().GetLog().SetLogLevel(mask);
		}

		else	if	(arg == "-debug")
					active_debug = true;

		else	if	(arg == "-ignore_bootstrap")
					ignore_bootstrap = true;
		else	if	(arg == "-ignore_esc")
					ignore_esc = true;
		else	if	(arg == "-ignore_missing")
					ignore_missing_resource = true;

		else	if	(arg == "-tool_mode")
					tool_mode = true;

		else	if	(arg == "-fallback_disk_fs")
					Platform::Get().io->Mount(new IO::CFile);

		else	if	(arg == "-safe_mode")
					safe_mode = true;

		else	if	(arg == "-enable_profiler")
					enable_profiler = true;
		else	if	(arg == "-memory_profiler")
					memory_profiler = true;

		else	if	(arg == "-enable_pause")
					enable_pause = true;

		// Config path.
		else	if	(arg == "-C")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-C: Missing configuration file path.\n", false);
			config_path = _arg[n];
		}

		// File system source.
		else	if	(arg == "-f")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-f: Missing root path.\n", false);
			session_source_path = _arg[n];
			session_source = SessionSourceFilesystem;
		}

		// Archive source.
		else	if	(arg == "-A")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-A: Missing archive path.\n", false);
			session_source_path = _arg[n];
			session_source = SessionSourceArchive;
		}

		// Renderer.
		else	if	(arg == "-r")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-r: Missing renderer id.\n", false);
			s_render = _arg[n];
		}

		else	if	(arg == "-w")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-w: Missing width value.\n", false);
			width = String::atoi(_arg[n]);
		}
		else	if	(arg == "-h")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-h: Missing height value.\n", false);
			height = String::atoi(_arg[n]);
		}

		// Mixer.
		else	if	(arg == "-m")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-m: Missing mixer id.\n", false);
			s_mixer = _arg[n];
		}

		// Search path.
		else	if	(arg == "-S")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-S: Missing search path.\n", false);

			Platform::Get().io->Mount(new IO::CFile(_arg[n]));
		}

		// Mount core.
		else	if	(arg == "-CC")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-CC: Missing core path.\n", false);

			String c_path = _arg[n];
			Platform::Get().io->Mount(new IO::CFile(c_path), "@core/");
Platform::Get().io->Mount(new IO::CFile(c_path));	// FIXME
		}

		// Mount point.
		else	if	(arg == "-MD")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-MD: Missing directory path.\n", false);
			String dir_path = _arg[n];

			if	(++n == narg)
				__ERR__(__LOG_E__ << "-MD: Missing mount point.\n", false);
			String mount_point = _arg[n];

			Platform::Get().io->Mount(new IO::CFile(dir_path), mount_point);
		}

		// Raytrace path.
		else	if	(arg == "-R")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-R: Missing raytracer output path.\n", false);
			raytrace_path = _arg[n];
		}

		// Raytrace width.
		else	if	(arg == "-Rw")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-Rw: Missing raytracer frame width.\n", false);
			raytrace_width = String::atoi(_arg[n]);
		}

		// Raytrace height.
		else	if	(arg == "-Rh")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-Rh: Missing raytracer frame height.\n", false);
			raytrace_height = String::atoi(_arg[n]);
		}

		// Raytrace AA.
		else	if	(arg == "-Raa")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-Raa: Missing raytracer AA grid size.\n", false);
			raytrace_aa = String::atoi(_arg[n]);
		}

		// Include.
		else	if	(arg == "-I")
		{
			if	(++n == narg)
				__ERR__(__LOG_E__ << "-I: Missing include path.\n", false);

			if	(!include_list.Add(_arg[n]))
				__ERR__(__LOG_E__ << "Failed to allocate include structure.\n", false);
		}

		// Variable.
		else	if	(arg == "-Ds")
		{
			n += 2;
			if	(n == narg)
				__ERR__(__LOG_E__ << "-Ds: Incomplete key-value pair. (eg. -Ds my_var \"String Value\")\n", false);
			Variant *yo = new Variant(_arg[n - 1], _arg[n]);
			if	(!define_list.Add(new Variant(_arg[n - 1], _arg[n])))
				__ERR__(__LOG_E__ << "Failed to allocate define structure.\n", false);
		}

		// Variable.
		else	if	(arg == "-Di")
		{
			n += 2;
			if	(n == narg)
				__ERR__(__LOG_E__ << "-Di: Incomplete key-value pair. (eg. -Di my_var 5)\n", false);

			if	(!define_list.Add(new Variant(_arg[n - 1], String::atoi(_arg[n]))))
				__ERR__(__LOG_E__ << "Failed to allocate define structure.\n", false);
		}

		// Variable.
		else	if	(arg == "-Df")
		{
			n += 2;
			if	(n == narg)
				__ERR__(__LOG_E__ << "-Df: Incomplete key-value pair. (eg. -Df my_var 5.5)\n", false);

			if	(!define_list.Add(new Variant(_arg[n - 1], String::atof(_arg[n]))))
				__ERR__(__LOG_E__ << "Failed to allocate define structure.\n", false);
		}

		else	if	(!OnUnknownCommandLineParam(_arg, n))
				__ERR__(__LOG_E__ << "Unknown command line parameter '" << arg << "'\n", false);
	}
	return true;
}
//-----------------------------------------------------------------------------
