#include "psmod.h"

#define	DEBUG_INPUT	"test.psdl"
#define	LOG_LEVEL	LOG_INFO

char	*running_script_name = "NUL";
char	*phase_component_names[PHASESPACE_COMPONENTS] = {"position","velocity"};

#if 0

/* implement and move to core */

#define	US_LEN	16

u32_t V_CALL safe_overwrite_destroy(...)
{
	/* do actual work */
	dest_name[_MAX_PATH];
	temp_name[_MAX_PATH];
	fileControl(self,pctl_get_name,dest_name);
	strCopy(temp_name,dest_name,_MAX_PATH);
	for(count;count < max_tries;count++)
	{
		archUniqueName(temp_name + strlen(dest_name),US_LEN);
		if(archFileExists(temp_name))
			continue;
		tmp_out = fileOpenWrite(temp_name);
		if(tmp_out != LIBV_INVALID_HANDLE)
			break;
	}
	fileControl(self,pctl_pipe_head,&ptr);
	fileControl(self,pctl_pipe_head_bytes,&nb);
	if(nb)
		fileWrite(tmp_out,ptr,nb);
	fileControl(self,pctl_pipe_tail,&ptr);
	fileControl(self,pctl_pipe_tail_bytes,&nb);
	if(nb)
		fileWrite(tmp_out,ptr,nb);
	fileClose(tmp_out);
	if(archFileExists(dest_name))
		archDeleteFile(dest_name);
	if(!archMoveFile(temp_name,dest_name))
	{
		/* unable to rename: tell user where to find data */
	}
}

vhandle_t V_GLOBAL
fileOpenSafeOverwrite(char *file_name)
{
#ifdef	__DOS32__
	return fileOpenWrite(file_name); /* for what to be safe ? :) */
#else
	vhandle_t handle;
	if(!archFileExists(file_name))
		handle = fileOpenWrite(file_name);
	if(handle != LIBV_INVALID_HANDLE)
		return handle;
	/* check for space for unique tail */
	if(file_name && strlen(file_name) > (_MAX_PATH - US_LEN))
	{
		conlPrintf(LOG_ERROR,"fileOpenSafeOverwrite(%s): file name too long.\n",file_name);
		return LIBV_INVALID_HANDLE;
	}
	handle = ioCreatePrivateNamedPipe(file_name);
	if(handle != LIBV_INVALID_HANDLE)
		(libv_object_p(handle))->destroy = safe_overwrite_destroy;
	return handle;
#endif
}

#endif

vboolean
psmodLoadPhase(char *file_name,vec_t *phase,u32_t num_particles,vec_t *time_ret)
{
	return phaseLoad(file_name,running_script_name,time_ret,phase,
		PHASESPACE_COMPONENTS,num_particles,phase_component_names);
}

vboolean
psmodSavePhase(char *file_name,vec_t *phase,u32_t num_particles,vec_t time)
{
	return phaseSave(file_name,running_script_name,time,phase,
		PHASESPACE_COMPONENTS,num_particles,phase_component_names);
}

vboolean
psmodSimulate(particle_system_p ps)
{
#ifdef	CHECKED_BUILD
	static char *caller = "psmodSimulate";
#endif	/* CHECKED_BUILD */
	vboolean integration_result;
	solver_stage_p stage = &ps->solver.current_stage;
	solver_p solver = &ps->solver;
	if(!fifoCount(&solver->stages))
	{
		ERROR_GENERIC("no solver stages defined");
		return false;
	}
	solver->solver_handle = odeCreateSolver(
		PHASESPACE_DIM * stackCount(&ps->particles),
		solver->right_side,solver->verbose);
	if(solver->solver_handle == LIBV_INVALID_HANDLE)
	{
		ERROR_GENERIC("error creating ODE solver");
		return false;
	}
	while(fifoPop(&solver->stages,&stage))
	{
		if(solver->current_time >= stage->upper_time)
		{
			ERROR_INVALID_FLOAT("stage->upper_time",stage->upper_time);
			return false;
		}
		odeSetTimeout(solver->solver_handle,stage->RK_timeout);
		if(stage->load_file[0])
		{
			if(!psmodLoadPhase(stage->load_file,ps->phase,
				stackCount(&ps->particles),&solver->current_time))
			{
				ERROR_INVALID_STR("error loading phase from",stage->load_file);
				return false;
			}
		}
		if(IsZero(stage->monitor_cps))
		{
			WARNING_GENERIC("zero 'monitor_cps'");
			return false; // fixme
		}
		else
		{
			odeSetMonitor(solver->solver_handle,solver->main_monitor_routine,
				ps,(vec_t)1.0 / stage->monitor_cps,
				stage->exact_monitoring);
		}
		timerStart(solver->rt_timer);
		integration_result = odeIntegrate(solver->solver_handle,
			ps->phase,
			solver->current_time,
			stage->upper_time,
			stage->RK_tolerance,
			stage->initial_step,
			stage->minimal_step,ps);
		timerStop(solver->rt_timer);
		if(stage->save_file[0])
		{
			if(!psmodSavePhase(stage->save_file,ps->phase,
				stackCount(&ps->particles),solver->current_time))
			{
				ERROR_INVALID_STR("error saving phase to",stage->save_file);
				return false;
			}
		}
		if(!integration_result)
			break;
	}
	if(!integration_result)
	{
		/* integration terminated ??? fixme */
	}
	return true;
}

#ifndef DEBUG_INPUT
int _main(int argc, char *argv[])
{
#else	/* DEBUG_INPUT */
int _main(void)
{
	CALLER_NAME(main)
	int argc=2;
	char *argv[]={"psmod",DEBUG_INPUT};
#endif	/* DEBUG_INPUT */
	vboolean result;
	particle_system_p ps;
	logStartConsoleLog();
	logUpperLevel(LOG_LEVEL);
	if(argc != 2)
	{
		psdlHelp(argv[0],"--help");
		return -1;
	}
	if(!strncasecmp(argv[1],"--help",6))
		return psdlHelp(argv[0],argv[1]);
	ps = psAlloc();
	if(!psdlParseScript(argv[1],ps))
	{
		psDestroy(ps);
		ERROR_INVALID_STR("wrong script file",argv[1]);
		return -1;
	}
	running_script_name = argv[1];
	result = psmodSimulate(ps);
	psDestroy(ps);
#ifdef	DEBUG_INPUT	
	memPrintStats();
	memPrintMemStats();
	memPrintObjStats();
#endif	/* DEBUG_INPUT */
	return result ? 0 : -1;
}

extern void icsg_main(void);

int main(void)
{
	logStartConsoleLog();
	logUpperLevel(LOG_TRACE);
	
	/*
	{
		vec3_t res;
		vhandle_t sc = lexParseString("(const [1,2,3])","()[]");
		vec3_controller_p ctl;
		ctl = ctlParseVec3(sc);
		CALL_VEC3_CTL(ctl,NULL,res);
		printf("res: [%g,%g,%g]\n",res[0],res[1],res[2]);
		ctlFree(ctl);
		lexDestroy(sc);
	}
	*/
	
	/*
	{
		char *fnames[] = {"pos","vel"};
		// vec_t phase[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
		// phaseSave("test.phase","somesrc",10,phase,2,2,fnames);
		vec_t phase[12];
		vec_t intime;
		if(phaseLoad("test.phase",NULL,&intime,phase,2,2,fnames))
			phaseSave("dupe.phase","somesrc",intime,phase,2,2,fnames);
	}
	*/
	icsg_main();

	memPrintStats();
	memPrintMemStats();
	memPrintObjStats();
	return 0;
}
