/***************************** 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"

class ShellFunction : public Function {
public:
	ShellFunction(char *n) : Function(n) //,1,tstring)
	{ info="Execute a shell command";};
	virtual Value Execute(int arity,Value *arg);
};

Value ShellFunction::Execute(int arity,Value *arg)
{
        int ret_code;
	const char *p;
	char buf[2048],shell_cmd[600];
	buf[0] = shell_cmd[0] = 0;

	for(int i = 0; i<arity;i++)
	{
		arg[i].GetValue(p);
		strcat(shell_cmd,p);
	}

	strcat(shell_cmd," 2>&1");

	FILE *f = popen(shell_cmd,"r");
	if(!f) return Error("Cannot execute shell command: %s",shell_cmd);

	while(fgets(buf,sizeof(buf),f))
		cout << buf;

	cout << flush;

	if ( (ret_code = pclose(f)) != 0 )
	  {
	    marslog(LOG_INFO,"Warning : Shell command '%s' returned %d",
		    shell_cmd,ret_code);
	  }

	return Value(ret_code);
}


class GetenvFunction : public Function {
public:
	GetenvFunction(char *n) : Function(n,1,tstring)
	{ info = "Return the value of a shell variable";};
	virtual Value Execute(int arity,Value *arg);
};

Value GetenvFunction::Execute(int ,Value *arg)
{
	const char *p;
	arg[0].GetValue(p);
	p=getenv(p);
	p=p?p:"";

	if(isdate(p))
		return Value(Date(p));
	else if(is_number(p))
		return Value(atof(p));
	else return Value(p);
}


class Getenv2Function : public Function {
public:
	Getenv2Function(char *n) : Function(n,2,tstring,tnumber)
	{ info = "Return the value of a shell variable";};
	virtual Value Execute(int arity,Value *arg);
};

Value Getenv2Function::Execute(int ,Value *arg)
{
	const char *p;
	arg[0].GetValue(p);
	p=getenv(p);
	p=p?p:"";

	int convert;
	arg[1].GetValue(convert);

	if(!convert)
		return Value(p);


	if(isdate(p))
		return Value(Date(p));
	else if(is_number(p))
		return Value(atof(p));
	else return Value(p);
}


class PutenvFunction : public Function {
public:
	PutenvFunction(char *n) : Function(n,2,tstring,tstring)
	{info = "Sets the value of an environment variable.";}
	virtual Value Execute(int arity,Value *arg);
};

Value PutenvFunction::Execute(int ,Value *arg)
{
	const char *p = NULL
	         , *q = NULL;

	arg[0].GetValue(p);
	arg[1].GetValue(q);
	if( !p && !q )
	  return Value((char *) NULL);

	char *buf = new char[strlen(p)+strlen(q)+2];  //memory leak!!!
	sprintf(buf, "%s=%s",p,q);
	putenv(buf);
	return Value(buf);
}


class SleepFunction : public Function {
public:
	SleepFunction(char *n) : Function(n,1,tnumber)
	{ info = "Sleep (number of seconds)";};
	virtual Value Execute(int arity,Value *arg);
};

Value SleepFunction::Execute(int ,Value *arg)
{
	double d;
	arg[0].GetValue(d);
	return Value(sleep((unsigned int)d));
}


class NiceFunction : public Function {
public:
	NiceFunction(char *n) : Function(n,1,tnumber)
	{ info = "Nice (nice value)";};
	virtual Value Execute(int arity,Value *arg);
};

Value NiceFunction::Execute(int ,Value *arg)
{
	int d;
	arg[0].GetValue(d);
	return Value(nice(d));
}


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

Value PauseFunction::Execute(int,Value*)
{
	return Value(pause());
}


class TempFileFunction : public Function {
public:
	TempFileFunction(char *n) : Function(n,0)
            { info = "Return a unique filename for a temporary file.";};
	virtual Value Execute(int arity,Value *arg);
};

Value TempFileFunction::Execute(int,Value*)
{
   char nam[1024]; //-- std::string cannot be used: mkstemp requires char array

   strcpy(nam,getenv("METVIEW_TMPDIR"));
   strcat(nam,"/macrotmp_XXXXXX");

   int ret = mkstemp(nam);                   //-- filename created + file opened

   if( ret != -1 )
      close(ret);                            //-- success, close created file
   else
      marslog(LOG_EROR,"tmpfile: unable to create a unique name!");

   return Value(nam);
}


static void install(Context *c)
{
	c->AddFunction(new ShellFunction("shell"));
	c->AddFunction(new SleepFunction("sleep"));
	c->AddFunction(new NiceFunction("nice"));
	c->AddFunction(new PauseFunction("pause"));
	c->AddFunction(new GetenvFunction("getenv"));
	c->AddFunction(new Getenv2Function("getenv"));
	c->AddFunction(new PutenvFunction("putenv"));
	c->AddFunction(new TempFileFunction("tmpfile"));
}

static Linkage linkage(install);

