/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include "macro.h"

// the 'memory' function does not compile on the Mac
#if !defined(__APPLE__) || !defined(__MACH__)
    #define COMPILE_MEMORY_FUNCTION
#endif

#ifdef COMPILE_MEMORY_FUNCTION
#include <malloc.h>
#endif

#include <inc_iostream.h>
#include <MvStopWatch.h>


void Context::Dump(int level)
{

	cout << "### Dump of context " << Name() << endl;

	if(argc) cout << "# Arguments:\n";

	for(int i = 0;i<argc;i++)
	{
		cout << "# ";
		argv[i].Dump(level); cout << endl;
	}

	Variable *v = (Variable*)Locals.Head();

	if(v) cout << "# Local variables:\n";

	while(v)
	{
		if(v->Name()[0] != '.')
		{
			cout << "# ";
			v->Dump(level); cout << endl;
		}
		v = (Variable*)v->Next();
	}

	v = (Variable*)Globals.Head();
	if(v) cout << "# Global variables:\n";

	while(v)
	{
		cout << "# ";
		v->Dump(level); cout << endl;
		v = (Variable*)v->Next();
	}
}

//=============================================================================

class TraceFunction : public Function {
public:
	TraceFunction(char *n) : Function(n,1,tnumber)
    {info = "Sets program execution tracing on/off";}
	virtual Value Execute(int,Value*);
};

Value TraceFunction::Execute(int,Value *arg)
{
	int n = Context::Trace();
	int m;

	arg[0].GetValue(m);
	Context::Trace(m);

	return Value(n);
}

//=============================================================================

class TraceFileFunction : public Function {
public:
	TraceFileFunction(char *n) : Function(n,1,tstring) {}
	virtual Value Execute(int,Value*);
};

Value TraceFileFunction::Execute(int,Value *arg)
{
	const char *m;
	arg[0].GetValue(m);

	ofstream* f = new ofstream(m);

	cout.rdbuf( f->rdbuf() ); // Memory leeak here

	return Value(m);
}

//=============================================================================
//=============================================================================

class TimerFunction : public Function {
public:
	TimerFunction(char *n) : Function(n,1,tnumber) {}
	virtual Value Execute(int,Value*);
};

Value TimerFunction::Execute(int,Value *arg)
{
	int m;
	arg[0].GetValue(m);

	if(m)
	{
		start_timer();
		return Value();
	}
	else
	{
		char info[1024];
		stop_timer(info);
		if (*info)
			return Value(info);
		else
			return Value("Time interval to small (less than 1 sec)");
	}
}
//=============================================================================

class StopWatchFunction : public Function {
public:
	StopWatchFunction(char* n, int a) : action(a), Function(n)
			 { info = "Provides stopwatch timings"; }
	virtual int ValidArguments(int arity,Value *arg);
	virtual Value Execute(int,Value*);
private:
	int action;
	static MvStopWatch* watch;
};

MvStopWatch* StopWatchFunction::watch = 0;

int StopWatchFunction::ValidArguments(int arity,Value *arg)
{
	if( arity > 1 )
	    return false;
	if( action!=4 && arg[0].GetType() != tstring )
	   return false;
	return true;
}

Value StopWatchFunction::Execute(int,Value *arg)
{
	const char* txt;
	arg[0].GetValue(txt);

	switch( action )
	{
	  case 1:                    //-- stopwatch_start
		if( watch )
		{
		   marslog(LOG_WARN,"stopwatch_start - watch already running, replace old watch!");
		   delete watch;
		}
		watch = new MvStopWatch( txt );
		break;

	  case 2:                    //-- stopwatch_laptime
		if( ! watch )
		{
		   marslog(LOG_WARN,"stopwatch_laptime - watch not running, starting now!");
		   watch = new MvStopWatch( "tictac" );
		}
		else
		   watch->lapTime( txt );
		break;

	  case 3:                    //-- stopwatch_reset
		if( ! watch )
		{
		   marslog(LOG_WARN,"stopwatch_reset - watch not running, starting now!");
		   watch = new MvStopWatch( "tictac" );
		}
		else
		   watch->reset( txt );
		break;

	  case 4:                    //-- stopwatch_stop
		if( watch )
		{
		   delete watch;
		   watch = 0;
		}
		else
		   marslog(LOG_WARN,"stopwatch_stop - no watch running!");
		break;

	}
	return Value("");
}

//=============================================================================

class DumpFunction : public Function {
public:
	DumpFunction(char *n) : Function(n) {}
	virtual Value Execute(int,Value*);
};


Value DumpFunction::Execute(int arity,Value *arg)
{
	if(arity)
	{
		for(int i=0;i<arity;i++)
		  {
			cout << "dump : "; arg[i].Dump(1); cout << endl;
		  }
	}
	else
	{
		Context *c = (Context*)Context::Instruction->Owner();
		while(c)
		{
			c->Dump(1);
			c = (Context*)c->Owner();
		}
	}

	return Value(0.0);
}

//=============================================================================

class MemFunction : public Function {
public:
	MemFunction(char *n) : Function(n,0) {}
	virtual Value Execute(int,Value*);
};

/* extern "C" void print_alloc(FILE*,int,int); */

Value MemFunction::Execute(int ,Value *)
{
#ifdef COMPILE_MEMORY_FUNCTION
	static long last = 0;
	struct mallinfo minfo = mallinfo();

	long used = minfo.uordblks + minfo.usmblks;

	request *r = empty_request(0);

	set_value(r,"total","%d",minfo.arena);
	set_value(r,"used","%d",used);
	set_value(r,"free","%d",minfo.arena-used);
	set_value(r,"delta","%d",used - last);

	last = used;

	return Value(r);
#endif
}


static void install(Context *c)
{
	c->AddFunction(new DumpFunction("dump"));
	c->AddFunction(new TraceFunction("trace"));
	c->AddFunction(new TraceFileFunction("tracefile"));
	c->AddFunction(new TimerFunction("timer"));
	c->AddFunction(new MemFunction("memory"));
	c->AddFunction(new StopWatchFunction("stopwatch_start",  1));
	c->AddFunction(new StopWatchFunction("stopwatch_laptime",2));
	c->AddFunction(new StopWatchFunction("stopwatch_reset",  3));
	c->AddFunction(new StopWatchFunction("stopwatch_stop",   4));
}

static Linkage Link(install);

