#include <time.h>
#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"

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");
}

V_LOCAL char *psdl_syntax=
	"# comment\n"
	"random_seed <integer random_seed>\n"
	"number_of_particles <integer>\n"
	"particle_radius <uniform float_min float_max>\n"
	"particle_density <uniform float_min float_max>\n"
	"initial_filename filename\n"
	"(with 'preload-phase' all IC domain info will be ignored)\n"
	"particle_minimal_distance <float minimal_distance>\n"
	"particle_distribution_domain <sphere <vec3 pos> <float radius>\n"
	"\t| <box <vec3 pos> <vec3 extents> \\\n"
	"\t| <cylinder <vec3 lower_base_center> <float radius <float height>\n"
	"boundary_plane <vec3 plane_normal> <float dist> <float_controller signed_scalar_f_for_given_distance>\n"
	"boundary_sphere <vec3 center> <float radius> <float_controller signed_scalar_f_for_given_distance>\n"
	"binary_interaction <float_controller unsigned_scalar_f_for_given_distance>\n"
	"environmental_interaction <viscous <ETA> <vec3_controller ENV_V>>\n"
	"unary_force <vec3_controller force>\n"
	"unary_accel <vec3_controller accel>\n"
	"saves_per_second <integer saves_per_second>\n"
	"solution_time_limit <float time_limit>\n"
	"solution_realtime_limit <float realtime_limit>\n"
	"solution_RK_tolerance <float tolerance>\n"
	"solution_RK_timeout <float timeout>\n"
	"final_filename filename\n\n";

void help_PSDL(void)
{
	fwrite(psdl_syntax,strlen(psdl_syntax),1,stdout);
}

V_LOCAL void
runGoal(particle_system_p ps)
{
	ps->ode_solver = odeCreateSolver(ps->num_particles * PHASESPACE_DIM,
		problem_right_side,false);
	if(!ps->ode_solver)
		return;
	if(!monitorInit(ps))
	{
		odeDestroySolver(ps->ode_solver);
		ps->ode_solver = LIBV_INVALID_HANDLE;
		return;
	}
	odeSetMonitor(ps->ode_solver,ps->ode_monitor,
		ps->ode_monitor_arg,(vec_t)1.0 / ps->saves_per_second,true);
	odeSetReal(ps->ode_solver,ODE_TIMEOUT,ps->solution_RK_timeout);
	odeSetReal(ps->ode_solver,ODE_MAXIMAL_STEP,ps->solution_maximal_step);
	timerStart(ps->rt_timer);
	odeIntegrate(ps->ode_solver,
		ps->phase,
		ps->cur_time,
		ps->solution_time_limit,
		ps->solution_RK_tolerance,
		ps->solution_maximal_step * 1e-3, /* estimation for step */
		ps->solution_maximal_step * 1e-10, /* minimal allowed step */
		ps);
	timerStop(ps->rt_timer);
	monitorShutdown(ps);
	odeDestroySolver(ps->ode_solver);
	ps->ode_solver = LIBV_INVALID_HANDLE;
}

int main(int argc, char *argv[])
{
	int i;
	size_t leak;
	vboolean nodraw = false; /* FIXME */
	char *fname = NULL;
	char outdir[_MAX_PATH];
	char logname[_MAX_PATH];
	time_t t;
	struct tm *tm;
	particle_system_p ps;
	logStartConsoleLog();
	t = time(NULL);
	tm = localtime(&t);
	strftime(outdir,_MAX_PATH,"out-%Y-%m-%d-%Hh-%Mm-%Ss",tm);
	logUpperLevel(LOG_LEVEL);
	conPrintf("%s\n", PSMOD_DESCRIPTION);
	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;
			}
			conlPrintf(LOG_WARNING,"main(): unknown option \"%s\"\n", argv[i]);
		}
		else
		{
			if(fname)
				conlPrintf(LOG_WARNING,"main(): extra file name given: \"%s\", ignoring \"%s\".\n",
					argv[i], fname);
			fname = argv[i];
		}
	}
	if(!fname)
	{
		conlPrintf(LOG_ERROR,"main(): no file name given.\n");
		return -1;
	}
	sprintf(logname,"%s/psmod.log",outdir);
	logStartFileLog(logname, true);
	ps = parse_psdl_script(fname);
	if(ps)
	{
		strCopy(ps->outdir, outdir, _MAX_PATH);
		ps->nodraw = nodraw;
		runGoal(ps);
		psFree(ps);
	}
	else
		conlPrintf(LOG_ERROR,"main(): error parsing '%s'\n",fname);
	logStopFileLog();
	logStopConsoleLog();
	memStats(&leak,NULL,NULL);
	printf("psmod run completed: leak %u byte(s).\n", (u32_t)leak);
	return 0;
}
