#include <v_lib.h>
#include "config.h"
#include "psdl.h"
#include "util.h"
#include "psys.h"
#include "ctrl.h"
#include "rh_side.h"
#include "monitor.h"
#include "phasesp.h"
#include "build_date.h"

vboolean nofilelog = false;

void usage(char *me)
{
	conPrintf("Usage: %s <scriptfile>, where scriptfile is an PSDL script.\n",me);
/*	conPuts("For brief description of PSDL, try --help-psdl.\n"); */
	conPuts("Try --help-controllers | --help-monitors.\n");
}

void help_PSDL(void)
{
	/* TODO */
}

V_LOCAL void
runGoal(particle_system_p ps)
{
	CALLER_NAME(runGoal)
	vec_t step_min, step_max, step_guess;
	if(ps->solution_maximal_step > 0)
		step_max = ps->solution_maximal_step;
	else
		step_max = 1;
	if(ps->solution_minimal_step > 0)
		step_min = ps->solution_minimal_step;
	else
		step_min = step_max * 1e-10;
	step_guess = 0.1 * (step_min + step_max);
	conlPrintf(LOG_INFO, "%s(): minimal allowed step is %g, maximal is %g, initial is %g\n",
		caller_name,step_min,step_max,step_guess);
	ps->ode_solver = odeCreateSolver(ps->num_particles * PHASESPACE_DIM);
	if(!ps->ode_solver)
		return;
	if(!monitorInit(ps))
	{
		odeDestroySolver(ps->ode_solver);
		ps->ode_solver = LIBV_INVALID_HANDLE;
		return;
	}

	odeSetRightside(ps->ode_solver,vdsRightSide);
	odeSetPointer(ps->ode_solver,ode_rightside_arg,ps);
	odeSetBoolean(ps->ode_solver,ode_verbose,false);

	odeSetMonitor(ps->ode_solver,odeMonitor);
	odeSetPointer(ps->ode_solver,ode_monitor_arg,ps);
	odeSetReal(ps->ode_solver,ode_monitor_delta,1 / ps->saves_per_second);
	odeSetBoolean(ps->ode_solver,ode_monitor_exact,true);

	odeSetReal(ps->ode_solver,ode_timeout,ps->solution_RK_timeout);
	odeSetReal(ps->ode_solver,ode_maximal_step,step_max);
	odeSetReal(ps->ode_solver,ode_minimal_step,step_min);
	odeSetReal(ps->ode_solver,ode_initial_step,step_guess);
	odeSetReal(ps->ode_solver,ode_tolerance,ps->solution_RK_tolerance);

	timerStart(ps->rt_timer);
	odeIntegrate(ps->ode_solver,ps->phase,ps->cur_time,ps->solution_time_limit);
	timerStop(ps->rt_timer);

	monitorShutdown(ps);
	odeDestroySolver(ps->ode_solver);
	ps->ode_solver = LIBV_INVALID_HANDLE;
}

V_LOCAL int
damnImGood(void)
{
	size_t leak;
	memStats(&leak,NULL,NULL);
	leak -= sizeof(void_p);
	if(leak)
	{
		logUpperLevel(LOG_TRACE);
		conlPrintf(LOG_ERROR,"Memory leak detected: %u byte(s).\n", (u32_t)leak);
		memPrintObjStats();
		memPrintMemStats();
	}
	logStopConsoleLog();
	return leak;
}

int main(int argc, char *argv[])
{
	CALLER_NAME(main)
	int i;
	vboolean nodraw = false;
	char *fname = NULL;
	particle_system_p ps;
	logStartConsoleLog();
	logUpperLevel(LOG_LEVEL);
	conPrintf("Particle system simulation program ver. 0.9 (%s)\n"
		"Copyright (C) Vladimir Smirnov, 2003-2019.\n",BUILD_DATE);
	if(argc == 1)
	{
		usage(argv[0]);
		return -1;
	}
	for(i = 1;i < argc;i++)
	{
		if(argv[i][0] == '-' && argv[i][1] == '-')
		{
			if(!strcasecmp(argv[i],"--help"))
			{
				usage(argv[0]);
				return 0;
			}
			if(!strcasecmp(argv[i],"--help-psdl"))
			{
				help_PSDL();
				return 0;
			}
			if(!strcasecmp(argv[i],"--help-controllers"))
			{
				helpControllers();
				return 0;
			}
			if(!strcasecmp(argv[i],"--help-monitors"))
			{
				listMonitorRoutines();
				return 0;
			}
			if(!strcasecmp(argv[i],"--nodraw"))
			{
				nodraw = true;
				continue;
			}
			if(!strcasecmp(argv[i],"--nofilelog"))
			{
				nofilelog = true;
				continue;
			}
			conlPrintf(LOG_WARNING,"%s(): unknown option \"%s\"\n", caller_name, argv[i]);
		}
		else
		{
			if(fname)
				conlPrintf(LOG_WARNING,"%s(): extra file name given: \"%s\", ignoring \"%s\".\n",
					caller_name,
					argv[i], fname);
			fname = argv[i];
		}
	}
	if(!fname)
	{
		conlPrintf(LOG_ERROR,"main(): no file name given.\n");
		return -1;
	}
	ps = parse_psdl_script(fname);
	if(ps)
	{
		ps->nodraw = nodraw;
		runGoal(ps);
		psFree(ps);
	}
	else
		conlPrintf(LOG_ERROR,"main(): error parsing '%s'\n",fname);
	if(!nofilelog)
		logStopFileLog();
	return damnImGood();
}
